[Rd] as.list fails on functions with S3 classes
Martin Maechler
m@ech|er @end|ng |rom @t@t@m@th@ethz@ch
Thu Apr 29 09:09:49 CEST 2021
>>>>> brodie gaslam via R-devel
>>>>> on Thu, 29 Apr 2021 01:04:01 +0000 (UTC) writes:
>> On Wednesday, April 28, 2021, 5:16:20 PM EDT, Gabriel Becker <gabembecker using gmail.com> wrote:
>>
>> Hi Antoine,
>>
>> I would say this is the correct behavior. S3 dispatch is solely (so far as
>> I know?) concerned with the "actual classes" on the object. This is because
>> S3 classes act as labels that inform dispatch what, and in what order,
>> methods should be applied. You took the function class (ie label) off of
>> your object, which means that in the S3 sense, that object is no longer a
>> function and dispatching to function methods for it would be incorrect.
>> This is independent of whether the object is still callable "as a function".
>>
>> The analogous case for non-closures to what you are describing would be for
>> S3 to check mode(x) after striking out with class(x) to find relevant
>> methods. I don't think that would be appropriate.
> I would think of the general case to be to check `class(unclass(x))` on
> strike-out. This would then include things such as "matrix", etc.
> Dispatching on the implicit class as fallback seems like a natural thing
> to do in a language that dispatches on implicit class when there is none.
> After all, once you've struck out of your explicit classes, you have
> none left!
> This does happen naturally in some places (e.g. interacting with a
> data.frame as a list), and is quite delightful (usually). I won't get
> into an argument of what the documentation states or whether any changes
> should be made, but to me that dispatch doesn't end with the implicit
> class seems feels like a logical wrinkle. Yes, I can twist my brain to
> see how it can be made to make sense, but I don't like it.
> A fun past conversation on this very topic:
> https://stat.ethz.ch/pipermail/r-devel/2019-March/077457.html
Thank you, Gabe and Brodie.
To the OP, Gabe's advice to *NOT* throw away an existing class
is really important, and good code -- several examples in base R --
would really *extend* a class in such cases, i.e.,
function(x, ...) {
......
ans <- things.on(x, .....)
class(ans) <- c("foo", class(x)) #
ans
}
I don't have time to go in-depth here (teaching and other duties),
but I want to point you to one important extra point,
which I think you have not been aware:
S3 dispatch *does* look at what you see from class() *but* has
always done some extra things, notably for atomic and other
*base* objects. There's always been a dedicated function in R's C
code to do this, R_data_class2(), e.g., called from C
usemethod() called from R's UseMethod().
Since R 4.0.0, we have provided R function .class2() to give
the same result as the internal R_data_class2(), and hence
show the classes (in the correct order!) which are really for S3
dispatch.
The NEWS entry for that was
\item New function \code{.class2()} provides the full character
vector of class names used for S3 method dispatch.
Best,
Martin
> Best,
> B.
>> Also, as an aside, if you want your class to override methods that exist
>> for function you would want to set the class to c("foo", "function"), not
>> c("function", "foo"), as you had it in your example.
>>
>> Best,
>> ~G
>>
>> On Wed, Apr 28, 2021 at 1:45 PM Antoine Fabri <antoine.fabri using gmail.com>
>> wrote:
>>
>>> Dear R devel,
>>>
>>> as.list() can be used on functions, but not if they have a S3 class that
>>> doesn't include "function".
>>>
>>> See below :
>>>
>>> ```r
>>> add1 <- function(x) x+1
>>>
>>> as.list(add1)
>>> #> $x
>>> #>
>>> #>
>>> #> [[2]]
>>> #> x + 1
>>>
>>> class(add1) <- c("function", "foo")
>>>
>>> as.list(add1)
>>> #> $x
>>> #>
>>> #>
>>> #> [[2]]
>>> #> x + 1
>>>
>>> class(add1) <- "foo"
>>>
>>> as.list(add1)
>>> #> Error in as.vector(x, "list"): cannot coerce type 'closure' to vector of
>>> type 'list'
>>>
>>> as.list.function(add1)
>>> #> $x
>>> #>
>>> #>
>>> #> [[2]]
>>> #> x + 1
>>> ```
>>>
>>> In failing case the argument is dispatched to as.list.default instead of
>>> as.list.function.
>>>
>>> (1) Shouldn't it be dispatched to as.list.function ?
>>>
>>> (2) Shouldn't all generics when applied on an object of type closure fall
>>> back to the `fun.function` method before falling back to the `fun.default`
>>> method ?
>>>
>>> Best regards,
>>>
>>> Antoine
> ______________________________________________
> R-devel using r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
More information about the R-devel
mailing list