From jfox at mcmaster.ca  Sat Dec 11 18:51:01 2004
From: jfox at mcmaster.ca (John Fox)
Date: Sat Dec 11 18:51:07 2004
Subject: [Rd] RE: [R] Testing for S4 objects
In-Reply-To: <41BB231B.6030108@R-project.org>
Message-ID: <20041211175100.KMAS1919.tomts22-srv.bellnexxia.net@JohnDesktop8300>

Dear John,

Thanks for this. I'll give your new definition of isS4object() a try. The
function is used in my Rcmdr package, which provides a basic-statistics GUI
for R. When an object -- the result of executing a command -- is printed, I
test whether it is an S4 object to decide whether to use show() or print().

Regards,
 John

--------------------------------
John Fox
Department of Sociology
McMaster University
Hamilton, Ontario
Canada L8S 4M4
905-525-9140x23604
http://socserv.mcmaster.ca/jfox 
-------------------------------- 

> -----Original Message-----
> From: r-devel-bounces@stat.math.ethz.ch 
> [mailto:r-devel-bounces@stat.math.ethz.ch] On Behalf Of John Chambers
> Sent: Saturday, December 11, 2004 11:41 AM
> To: John Fox
> Cc: 'Martin Maechler'; r-devel@stat.math.ethz.ch
> Subject: Re: [Rd] RE: [R] Testing for S4 objects
> 
> 
> 
> John Fox wrote:
> > Dear r-devel list members,
> > 
> > I'm moving this question to r-devel because it seems 
> thornier than I 
> > originally thought.
> 
> Yes, it's certainly not for r-help.
> 
> > 
> > I've already mentioned (on r-help) that the approach that John 
> > Chambers suggested (below) fails for objects of class "by":
> > 
> > 
> >>x <- rnorm(100)
> >>y <- sample(2, 100, replace=TRUE)
> >>res <- by(x, y, mean)
> >>res
> > 
> > INDICES: 1
> > [1] -0.03429679
> > ------------------------------------------------------------
> > INDICES: 2
> > [1] -0.1273790
> > 
> >>class(res)
> > 
> > [1] "by"
> > 
> >>isS4object <- function(object)(length(attr(object, "class"))==1 &&
> > 
> > +     !is.null(getClass(class(object))))
> > 
> >>isS4object(res)
> > 
> > Error in getClass(class(object)) : "by" is not a defined class
> > 
> > 
> > I tried to fix that, but I've now discovered more general 
> problems; e.g.:
> 
> Let's not revert to slotNames(). For the reasons I mentioned 
> it's inevitably going to produce a confusing definition.
> 
> There are a couple of problems (aside from my having used 
> getClass() where I meant to use getClassDef() :-{):
> - we need to handle S3 classes that have been registered with 
> S4 dispatch by calling setOldClass().  Doing this is strongly 
> recommended, but the effect is to create an S4 definition.  
> That's one reason why
> lm() objects might appear to be S4 objects.  Presumably, we 
> don't want that.
> - eventually, if this is a serious thing that people need, we 
> need to worry about objects defined in namespaces, with 
> private class definitions.
> 
> Here's a more careful version of the previous idea, which I 
> believe handles the first of these problems, by using the 
> fact that an object generated by new("foo",...) cannot come 
> from a VIRTUAL class. That gets actually to "What do we 
> really mean by an S4 object?"  I'm essentially saying that an 
> object that could not have been created by a new() call is 
> not an S4 object.  People could cheat, of course, and it's 
> not clear what we should do with such objects.  Both the case 
> of no definition and the case of S3 classes registered with 
> setOldClass() should produce a VIRTUAL class.
> 
> (By the way, I was wondering what the actual intent of this 
> function was.  Usually, one would try to have generic 
> functions deal sensibly with objects for which they had no 
> method--either some default calculation or an error message.  
> The notion of "S4 object" is pretty general or vague, as 
> we're demonstrating.  On the whole, it would be better not to 
> get tangled up in it.)
> 
> I'm weakly confident that the current version also handles 
> the namespace issue, by using the actual class() call, which 
> should include a "package" attribute to get to the right namespace.
> 
> But no assertions that extensive testing has been done.  
> Nevertheless, here is a second approximation.
> 
> isS4Object <- function(object) {
>      if(length(attr(object, "class"))!= 1)
>          return(FALSE)
>     !isVirtualClass(getClass(class(object), TRUE)) }
> 
> 
> 
> > 
> > 
> >>mod <- lm(y ~ x)
> >>class(mod)
> > 
> > [1] "lm"
> > 
> >>isS4object(mod)
> > 
> > [1] TRUE
> > 
> >>class(summary(mod))
> > 
> > [1] "summary.lm"
> > 
> >>isS4object(summary(mod))
> > 
> > Error in getClass(class(object)) : "summary.lm" is not a 
> defined class
> > 
> > 
> > I've reverted to a modified version of my original proposal:
> > 
> > 
> >>isS4object <- function(object) {
> > 
> > +     !(length(object) == 1 && class(object) == "character") &&
> > length(slotNames(object)) != 0
> > +     }
> > 
> >>isS4object(res)
> > 
> > [1] FALSE
> > 
> >>isS4object(mod)
> > 
> > [1] FALSE
> > 
> >>isS4object(summary(mod))
> > 
> > [1] FALSE
> > 
> >># example from ?mle
> >>x <- 0:10
> >>y <- c(26, 17, 13, 12, 20, 5, 9, 8, 5, 4, 8) ll <- 
> function(ymax=15, 
> >>xhalf=6)
> > 
> > +     -sum(stats::dpois(y, lambda=ymax/(1+x/xhalf), log=TRUE))
> > 
> >>fit <- mle(ll)
> > 
> > Warning message: 
> > NaNs produced in: dpois(x, lambda, log)
> > 
> >>isS4object(fit)
> > 
> > [1] TRUE
> > 
> >>isS4object("mle")
> > 
> > [1] FALSE
> > 
> > 
> > All this is with R 2.0.1 under Windows NT.
> > 
> > Comments would be appreciated.
> > 
> > John
> > 
> > --------------------------------
> > John Fox
> > Department of Sociology
> > McMaster University
> > Hamilton, Ontario
> > Canada L8S 4M4
> > 905-525-9140x23604
> > http://socserv.mcmaster.ca/jfox
> > --------------------------------
> > 
> > 
> >>-----Original Message-----
> >>From: John Chambers [mailto:johnmchambers@gmail.com]
> >>Sent: Tuesday, November 30, 2004 9:40 AM
> >>To: John Fox
> >>Cc: Martin Maechler; r-help@stat.math.ethz.ch
> >>Subject: Re: [R] Testing for S4 objects
> >>
> >>Let me suggest a different test, because slotNames was 
> written to work 
> >>differently when given a string or a class definition.  With your 
> >>definition,
> >>
> >>R> x <- "classRepresentation"
> >>R> isS4object(x)
> >>[1] TRUE
> >>
> >>which I assume is not what you wanted.  (Given a single string,
> >>slotNames() tries to look up the class definition of that name.)
> >>
> >>How about the following?  The logic is that an S4 object 
> must have an 
> >>actual class attribute of length 1 (that rules out basic 
> data types, 
> >>where class(x) is a string but there is no actual 
> attribute, and also 
> >>rules out some S3 objects).  Then if that's true, try to 
> look up the 
> >>class definition.  If it is non-null, seems like an S4 object.
> >>
> >>R> isS4object <- function(object)(length(attr(object, 
> "class"))==1 &&
> >>+     !is.null(getClass(class(object))))
> >>R> isS4object(x)
> >>[1] FALSE
> >>R> isS4object(getClass(class(x)))
> >>[1] TRUE
> >>
> >>This definition seems to work, at least on the examples I 
> could think 
> >>of right away.  Notice though, that some classes, such as 
> "ts", that 
> >>have been around for a long while are nevertheless legitimate S4 
> >>classes, so:
> >>
> >>R> t1 = ts(1:12)
> >>R> isS4object(t1)
> >>[1] TRUE
> >>
> >>(this applies to either version of isS4object).
> >>
> >>There are a couple of details, more appropriate for the 
> r-devel list. 
> >>Seems  a good candidate for a function to add to R.
> >>
> >>
> >>On Sat, 27 Nov 2004 17:48:30 -0500, John Fox 
> <jfox@mcmaster.ca> wrote:
> >>
> >>>Dear Martin,
> >>>
> >>>As it turns out, the test that I proposed (i.e., testing for NULL
> >>>slotNames) sometimes fails. For example:
> >>>
> >>>
> >>>>library(car)
> >>>>data(Prestige)
> >>>>sum <- summary(lm(prestige ~ income + education, data=Prestige))
> >>>>slotNames(sum)
> >>>
> >>>character(0)
> >>>
> >>>The following, however, seems to work (at least as far as 
> I've been 
> >>>able to
> >>>ascertain):
> >>>
> >>>isS4object <- function(object) length(slotNames(object)) != 0
> >>>
> >>>I hope that this is a more robust test.
> >>>
> >>>
> >>>
> >>>John
> >>>
> >>>--------------------------------
> >>>John Fox
> >>>Department of Sociology
> >>>McMaster University
> >>>Hamilton, Ontario
> >>>Canada L8S 4M4
> >>>905-525-9140x23604
> >>>http://socserv.mcmaster.ca/jfox
> >>>--------------------------------
> >>>
> >>>
> >>>>-----Original Message-----
> >>>>From: Martin Maechler [mailto:maechler@stat.math.ethz.ch]
> >>>>Sent: Friday, November 26, 2004 3:18 AM
> >>>>To: John Fox
> >>>>Cc: r-help@stat.math.ethz.ch
> >>>>Subject: Re: [R] Testing for S4 objects
> >>>>
> >>>>
> >>>>>>>>>"JohnF" == John Fox <jfox@mcmaster.ca>
> >>>>>>>>>    on Thu, 25 Nov 2004 22:28:50 -0500 writes:
> >>>>
> >>>>    JohnF> Dear r-help list members, Is there a way to test
> >>>>    JohnF> whether an object is an S4 object? The best that I've
> >>>>    JohnF> been able to come up with is
> >>>>
> >>>>    JohnF>    isS4object <- function(object)
> >>>>!(is.null(slotNames(object)))
> >>>>
> >>>>you can drop one pair of "(..)" to give
> >>>>
> >>>>  isS4object <- function(object) !is.null(slotNames(object))
> >>>>
> >>>>
> >>>>    JohnF> which assumes that an S4 object has at least one
> >>>>    JohnF> slot. I think this is safe, but perhaps I'm missing
> >>>>    JohnF> something.
> >>>>
> >>>>The question is a very good one -- that I have posed to R-core a 
> >>>>while ago myself.
> >>>>
> >>>>Inside  utils:::str.default  {which doesn't show the many
> >>
> >>commments
> >>
> >>>>in the *source* of str.default()}, I have wanted a way that even 
> >>>>works when the 'methods' package is not attached and use the more 
> >>>>obscure
> >>>>
> >>>>    #NOT yet:if(has.class <- !is.null(cl <- class(object)))
> >>>>    if(has.class <- !is.null(cl <- attr(object, "class")))#
> >>>>S3 or S4 class
> >>>>      S4 <- !is.null(attr(cl, "package"))## <<<'kludge' FIXME!
> >>>>      ##or length(methods::getSlots(cl)) > 0
> >>>>
> >>>>For the time being, I'd keep your function, but I don't
> >>
> >>think we'd
> >>
> >>>>guarantee that it will remain the appropriate test in all
> >>
> >>future.  
> >>
> >>>>But till then many things will have happened (if not all of them 
> >>>>;-).
> >>>>
> >>>>Martin Maechler, ETH Zurich
> >>>>
> >>>
> >>>______________________________________________
> >>>R-help@stat.math.ethz.ch mailing list 
> >>>https://stat.ethz.ch/mailman/listinfo/r-help
> >>>PLEASE do read the posting guide! 
> >>>http://www.R-project.org/posting-guide.html
> >>>
> > 
> > 
> > ______________________________________________
> > R-devel@stat.math.ethz.ch mailing list 
> > https://stat.ethz.ch/mailman/listinfo/r-devel
> >
> 
> ______________________________________________
> R-devel@stat.math.ethz.ch mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel


[Rd] RE: [R] Testing for S4 objects
John Fox