This is because the class is "f", not c("f", "function") in your second
example. S3 method dispatch is doing what you (unintentionally, I presume)
asked it to do.
The S3 method which allows head to take functions is utils:::head.function.
S3 can only be expected to understand inheritance if the value of class(f)
is multivalued. It doesn't have any way of knowing f is still a function
after the class assignment because "function" does not appear anywhere in
the class vector, so instead of hitting utils:::head.function, it hits
utils:::head.default, which uses [ on the argument, causing the error.
I'd say this is "expected" behavior within the context of the S3 system.
I also see this behavior at least as far aback as 3.5.1, so its not new to
> (Using R 3.5.3).
> I found bugs in head() and tail().
> The following works:
> > f = function () 1
> > head (f)
> 1 function ()
> 2 1
> However, the following does not:
> > class (f) = "f"
> > head (f)
> Error in x[seq_len(n)] : object of type 'closure' is not subsettable
> [[alternative HTML version deleted]]
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel >
> In the case of head.default(), it assumes that the object is a vector, or
> something similar.
No it doesn't. It assumes (ultimately) that x[seq_len(n)] is the correct
way to generate a "head" of something. Which is reasonable. That's
dependent on the implementation of the `[` method on object `x`.
> Resulting in the error above, which fails to recognize that the input was
> Because the object you fed it didn't have a method for `head` or for `[`.
Its the object designer's responsibility to do that. And by creating a
function that doesn't have the "function" class - that means *you*.
There's no way that head.default can inspect everything it might get fed,
including classes that have yet to be made, to see if it can do anything
meaningful with it. So it uses what looks like a reasonable approach -
apply `[` on the object with the first `n` elements. Let the class decide
what to do with it.
You might think generics should trap errors and give meaningful errors, but
the error isn't in the generic here - its in the `[` method of the class it
was fed. `head` doesn't know or care about that - again, its the class
designers responsibility to handle that, and the users responsibility to
*not* call generics on objects for which there's no meaningful behaviour.
It's really the error message of `[.default` seeming obscure to you that is
your issue here. If it said "cannot create subsets of this object with ["
would that please you? Again, that's not a bug in `head` or anything to do
with method dispatch.
So lets take a look at your choices here:
"If a generic has a default method, then that default method should be
guaranteed to work."
- as demonstrated, the default here does its job. it does "work".
"Or at least, provide a useful error message, that makes it obvious to the
user, what he or she has done wrong."
and that error message is the responsibility of methods of the object, in
this case `[`.