8

R's S3 OO system is centered around generic functions that call methods depending on the class of the object the generic function is being called on. The crux is that the generic function calls the appropriate method, as opposed to other OO programming languages in which the method is defined within the class.

For example, the mean function is a generic function.

isGeneric("mean")
methods(mean)

This will print

TRUE
[1] mean,ANY-method          mean.Date                mean.default             mean.difftime           
[5] mean.IDate*              mean,Matrix-method       mean.POSIXct             mean.POSIXlt            
[9] mean,sparseMatrix-method mean,sparseVector-method
see '?methods' for accessing help and source code

I was exploring R a bit and found the as function. I am confused by the fact that R says the function is not generic, but it still has methods.

isGeneric("as")
methods(as)

TRUE
  [1] as.AAbin                                  as.AAbin.character                       
  [3] as.alignment                              as.allPerms                              
  [5] as.array                                  as.array.default                         
  [7] as.binary                                 as.bitsplits                             
  [9] as.bitsplits.prop.part                    as.call
  ...                                  

At the end there is a warning that says that as is not a generic.

 Warning message:
 In .S3methods(generic.function, class, parent.frame()) :
 function 'as' appears not to be S3 generic; found functions that look like S3 methods

Could someone explain me what the as function is and how is connected to as.list, as.data.frame etc? R says that as.list is a generic (where I am tempted to get a bit mad at the inconsistencies within R, because I would expect as.list to be a method for a list object from the as generic function). Please help.

smci
  • 32,567
  • 20
  • 113
  • 146
PejoPhylo
  • 469
  • 2
  • 11
  • 1
    From `?as`, `The S3 functions listed are those which are named like methods and may not actually be methods (known exceptions are discarded in the code).` If you look at the source for `as`, it doesn't look like an S3 generic (like `mean`) with just a `UseMethod` call, which reinforces the claim that it isn't a generic. That's only part of the story, though; there's definitely more to be told here. – alistaire Jan 14 '18 at 18:08
  • 3
    Two somewhat related questions, with interesting discussions in the comments: [as(x, 'double') and as.double(x) are inconsistent](https://stackoverflow.com/questions/34093056/asx-double-and-as-doublex-are-inconsistent), [Why, for an integer vector x, does as(x, “numeric”) trigger loading of an additional S4 method for coerce?](https://stackoverflow.com/questions/34141757/why-for-an-integer-vector-x-does-asx-numeric-trigger-loading-of-an-additi) – Henrik Jan 14 '18 at 18:14
  • 1
    Oops, the above quote is from `?methods`, not `?as`. – alistaire Jan 14 '18 at 18:51

1 Answers1

11

as is not an S3 generic, but notice that you got a TRUE. (I got a FALSE.) That means you have loaded a package that definesas as an S4-generic. S3-generics work via class dispatch that employs a *.default function and the UseMethod-function. The FALSE I get means there is no method defined for a generic as that would get looked up. One arguable reason for the lack of a generic as is that calling such a function with only one data object would not specify a "coercion destination". That means the destination needs to be built into the function name.

After declaring as to be Generic (note the capitalization which is a hint that this applies to S4 features:

setGeneric("as")  # note that I didn't really even need to define any methods

get('as')
#--- output----
standardGeneric for "as" defined from package "methods"

function (object, Class, strict = TRUE, ext = possibleExtends(thisClass, 
    Class)) 
standardGeneric("as")
<environment: 0x7fb1ba501740>
Methods may be defined for arguments: object, Class, strict, ext
Use  showMethods("as")  for currently available ones.

If I reboot R (and don't load any libraries that call setGeneric for 'as') I get:

get('as')
#--- output ---
function (object, Class, strict = TRUE, ext = possibleExtends(thisClass, 
    Class)) 
{
    if (.identC(Class, "double")) 
        Class <- "numeric"
    thisClass <- .class1(object)
    if (.identC(thisClass, Class) || .identC(Class, "ANY")) 
        return(object)
    where <- .classEnv(thisClass, mustFind = FALSE)
    coerceFun <- getGeneric("coerce", where = where)
    coerceMethods <- .getMethodsTable(coerceFun, environment(coerceFun), 
        inherited = TRUE)
    asMethod <- .quickCoerceSelect(thisClass, Class, coerceFun, 
        coerceMethods, where)
    .... trimmed the rest of the code

But you ask "why", always a dangerous question when discussing language design, of course. I've flipped through the last chapter of Statistical Models in S which is the cited reference for most of the help pages that apply to S3 dispatch and find no discussion of either coercion or the as function. There is an implicit definition of "S3 generic" requiring the use of UseMethod but no mention of why as was left out of that strategy. I think of two possibilities: it is to prevent any sort of inheritance ambiguity in the application of the coercion, or it is an efficiency decision.

I should probably add that there is an S4 setAs-function and that you can find all the S4-coercion functions with showMethods("coerce").

IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • There's still an open question remaining here as to why coercion functions like `as.character` are named like S3 methods of `as` even though they aren't (and can't be, as they're structured). "Historical reasons", I'm sure, but I'm curious about the specifics of how R ended up with such a glaring inconsistency. – alistaire Jan 14 '18 at 19:47
  • 3
    I gave my theories but I think that is really an "Ask the authors" question and very few of them frequent SO. Perhaps an Rhelp query would be more informative. – IRTFM Jan 14 '18 at 19:49