Skip to content

promptClass misses methods

5 messages · Martin Maechler, Ross Boylan, Seth Falcon

#
RossB> I've had repeated problems with promptClass missing
    RossB> methods, usually telling me a class has no methods
    RossB> when it does.

    RossB> In my current case, I've defined an S4 class
    RossB> "mspathCoefficients" with a print method
    RossB> setMethod("print", signature(x="mspathCoefficients"),
    RossB> function(x, ...)  { # etc

You should *not* define "print" methods for S4 classes;
rather you should define "show" methods.

    RossB> The file promptClass creates has no methods in it.
    >> showMethods(classes="mspathCoefficients")
    RossB> Function: initialize (package methods)
    RossB> .Object="mspathCoefficients" (inherited from:
    RossB> .Object="ANY")

so it's just inherited from "ANY"

    RossB> Function: print (package base) 
    RossB> x="mspathCoefficients"

that's the one

    RossB> Function: show (package methods)
    RossB> object="mspathCoefficients"
    RossB>  (inherited from: object="ANY")
so it's just inherited from "ANY"

Ross, it would really be more polite to your readers if you
followed the posting guide and posted complete
fully-reproducible code...

    >> getGeneric("print")
    RossB> standardGeneric for "print" defined from package
    RossB> "base"

    RossB> function (x, ...)  standardGeneric("print")
    RossB> <environment: 0x84f2d88> Methods may be defined for
    RossB> arguments: x


    RossB> I've looked through the code for promptClass, but
    RossB> nothing popped out at me.

    RossB> It may be relevant that I'm running under ESS in
    RossB> emacs.  However, I get the same results running R
    RossB> from the command line.

    RossB> Can anyone tell me what's going on here?  This is
    RossB> with R 2.4, and I'm not currently using any namespace
    RossB> for my definitions.

[and not a package either?]

I'm very willing to look at this, once
you've provided what the posting guide asks for, see above.

Regards,
Martin
#
On Fri, Dec 01, 2006 at 11:37:46AM +0100, Martin Maechler wrote:
Is that because print is used by the S3 system?  And is the general
rule to avoid using S3 methods for S4 classes?  For example,
http://www.omegahat.org/RSMethods/Intro.pdf, which is referenced in
the package help for methods, discusses show, print and plot as 3
alternatives in S4 (p. 9, though a footnote says that at that
time--2001--R didn't recognize formal methods for printing objects.)

I've been unable to locate much information about combining S3 and S4
methods, though I recall seeing a note saying this issue was still to
be addressed in R.  Perhaps it has been now, with setOldClass?  At any
rate, the help for that method addresses classes rather than methods,
and I didn't see anything in ?Methods, ?setMethod, or ?setGeneric.

show() raises two additional issues for me.  First, it takes a single
argument, and I want to be able to pass in additional arguments via
... .  Second, I read some advice somewhere, that I no longer can find,
that show methods should return an object and that object in turn
should be the thing that is printed.  I don't understand the
motivation for that rule, at least in this case, because my object is
already a results object.
So why isn't promptClass picking it up?
I thought it might be overkill in this case.  At any rate, it sounds
as if I may be trying to do the wrong thing, so I'd appreciate
guidance on what the right thing to do is.

Here's a toy example:
setClass("A", representation(x="numeric"))
setMethod("print", signature(x="A"), function(x, ...) print(x at x, ...) )
promptClass("A")

The generated file has no print method.
The code is part of  a package, but I'm developing code snippets in
ESS without loading the whole package.
Thank you.  For completeness, here's some system info:
R version 2.4.0 (2006-10-03) 
i486-pc-linux-gnu 

locale:
LC_CTYPE=en_US;LC_NUMERIC=C;LC_TIME=en_US;LC_COLLATE=en_US;LC_MONETARY=en_US;LC_MESSAGES=en_US;LC_PAPER=en_US;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US;LC_IDENTIFICATION=C

attached base packages:
[1] "methods"   "stats"     "graphics"  "grDevices" "utils"     "datasets" 
[7] "base"     

The Debian package is r-base-core 2.4.0.20061103-1.

Ross
#
RossB> On Fri, Dec 01, 2006 at 11:37:46AM +0100, Martin
RossB> Maechler wrote:
>> >>>>> "RossB" == Ross Boylan <ross at biostat.ucsf.edu>
    >> >>>>> on Thu, 30 Nov 2006 22:29:06 -0800 writes:
    >> 
    RossB> I've had repeated problems with promptClass missing
    RossB> methods, usually telling me a class has no methods
    RossB> when it does.
    >>
    RossB> In my current case, I've defined an S4 class
    RossB> "mspathCoefficients" with a print method
    RossB> setMethod("print", signature(x="mspathCoefficients"),
    RossB> function(x, ...)  { # etc
    >>  You should *not* define "print" methods for S4 classes;
    >> rather you should define "show" methods.

    RossB> Is that because print is used by the S3 system?  

no, not really.
    RossB> And is the general rule to avoid using S3 methods for S4
    RossB> classes?

Well your wording is murky, but no, you *should* define (S4) methods 
for S3 generics very well.  The S3 generics are automagically
promoted to S4 generics as soon as you define an S4 method for it.

print() is just a big exception.

    RossB>   For example, http://www.omegahat.org/RSMethods/Intro.pdf, which is
    RossB> referenced in the package help for methods, discusses
    RossB> show, print and plot as 3 alternatives in S4 (p. 9,
    RossB> though a footnote says that at that time--2001--R
    RossB> didn't recognize formal methods for printing
    RossB> objects.)

2001 is way in the past concerning S4 implementation in R.
Specifically, using S4 in R; we'd   **very strongly** recommend
R 2.4.0 (and ideally even R-patched) because of several recent
good developments.

    RossB> I've been unable to locate much information about
    RossB> combining S3 and S4 methods, though I recall seeing a
    RossB> note saying this issue was still to be addressed in
    RossB> R.  Perhaps it has been now, with setOldClass?  At
    RossB> any rate, the help for that method addresses classes
    RossB> rather than methods, and I didn't see anything in
    RossB> ?Methods, ?setMethod, or ?setGeneric.

    RossB> show() raises two additional issues for me.  First,
    RossB> it takes a single argument, and I want to be able to
    RossB> pass in additional arguments via ... .  

That's not possible currently.

And I agree that in certain cases, I would want to have the
flexibility of print(..) there;
One case is for printing/showing fitted LMER objects; the
following code is used :

  ## This is modeled a bit after  print.summary.lm :
  printMer <- function(x, digits = max(3, getOption("digits") - 3),
		       correlation = TRUE, symbolic.cor = x$symbolic.cor,
		       signif.stars = getOption("show.signif.stars"), ...)
  {
	  ...............
	  ...............
      invisible(x)
  }

  setMethod("print", "mer", printMer)
  setMethod("show", "mer", function(object) printMer(object))


    RossB> Second, I read some advice somewhere, that I no
    RossB> longer can find, that show methods should return an
    RossB> object and that object in turn should be the thing
    RossB> that is printed.  I don't understand the motivation
    RossB> for that rule, at least in this case, because my
    RossB> object is already a results object.

I  think you're confusing show() with summary(): The latter is
should typically compute an object which then has a print/show method.


    RossB> The file promptClass creates has no methods in it.
    >> >> showMethods(classes="mspathCoefficients")
    RossB> Function: initialize (package methods)
    RossB> .Object="mspathCoefficients" (inherited from:
    RossB> .Object="ANY")
    >>  so it's just inherited from "ANY"
    >> 
    RossB> Function: print (package base) x="mspathCoefficients"
    >>  that's the one

    RossB> So why isn't promptClass picking it up?

    >>
    RossB> Function: show (package methods)
    RossB> object="mspathCoefficients" (inherited from:
    RossB> object="ANY")
    >> so it's just inherited from "ANY"
    >> 
    >> Ross, it would really be more polite to your readers if
    >> you followed the posting guide and posted complete
    >> fully-reproducible code...

    RossB> I thought it might be overkill in this case.  

It never is. We do want self-contained executable code..
In your case, I assumed it might have been things in a namespace
in a package, ...

    RossB> At any rate, it sounds as if I may be trying to do the wrong
    RossB> thing, so I'd appreciate guidance on what the right
    RossB> thing to do is.

    RossB> Here's a toy example:

    RossB> setClass("A", representation(x="numeric"))
    RossB> setMethod("print", signature(x="A"), function(x, ...) print(x at x, ...) )
    RossB> promptClass("A")

    RossB> The generated file has no print method.
 
Indeed, I can confirm that.

    >> >> getGeneric("print")
    RossB> standardGeneric for "print" defined from package
    RossB> "base"
    >> 
    RossB> function (x, ...)  standardGeneric("print")
    RossB> <environment: 0x84f2d88> Methods may be defined for
    RossB> arguments: x
    >> 
    >> 
    RossB> I've looked through the code for promptClass, but
    RossB> nothing popped out at me.
    >> 
    RossB> It may be relevant that I'm running under ESS in
    RossB> emacs.  However, I get the same results running R
    RossB> from the command line.
    >> 
    RossB> Can anyone tell me what's going on here?  This is
    RossB> with R 2.4, and I'm not currently using any namespace
    RossB> for my definitions.

    >> [and not a package either?]
    RossB> The code is part of  a package, but I'm developing code snippets in
    RossB> ESS without loading the whole package.
    >> 
    >> I'm very willing to look at this, once
    >> you've provided what the posting guide asks for, see above.

so now that I've promissed it; I'll have to look at it ;-)
Probably too late to get a fix into R 2.4.1 though.

Martin

    RossB> Thank you.  For completeness, here's some system info:

    >> sessionInfo()
    RossB> R version 2.4.0 (2006-10-03) 
    RossB> i486-pc-linux-gnu 

    RossB> locale:
    RossB> LC_CTYPE=en_US;LC_NUMERIC=C;LC_TIME=en_US;LC_COLLATE=en_US;LC_MONETARY=en_US;LC_MESSAGES=en_US;LC_PAPER=en_US;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US;LC_IDENTIFICATION=C

    RossB> attached base packages:
    RossB> [1] "methods"   "stats"     "graphics"  "grDevices" "utils"     "datasets" 
    RossB> [7] "base"     

    RossB> The Debian package is r-base-core 2.4.0.20061103-1.

    RossB> Ross
#
On Sat, Dec 02, 2006 at 05:11:22PM +0100, Martin Maechler wrote:
That answers my question.  The meaning was if "foo" is an S3 method,
should one avoid defining "foo" as an S4 method.  And the answer is
no, it's OK.  I assume one should strive to use the same argument
names, although since S3 methods don't need to use the same argument
names I'm not sure how that works (e.g. for S3
  foo.class1 <- function(x, a, b) but
  foo.class2 <- function(x, c)
).
How come?  

Is it an exception in the sense that it is not automatically used to
display the object, or in the sense that one should never define S4
print methods at all?  (Looks like the first alternative based on the
example later.)  The print methods I have seem to work OK, provided I
don't expect them to be called automatically and provided I don't
expect promptClass to pick them up.
Perhaps the reference should be removed then.  Maybe the newer
http://developer.r-project.org/howMethodsWork.pdf would be better?
However, that is pitched more toward the internals, and is already
referenced in ?Methods.
Fortunately that's what I'm using.  I wonder if this is so important I
should require R >= 2.4 for my package.  It was working fine in
earlier versions.  The main user visible changes I'm aware of are
those in the object forms (i.e., binary incompatibility) and some
improvements in the algorithm for choosing which method to dispatch to
(semantically, sometimes a different method gets called; it sounds
faster too).  I'm not distributing any data files with S4 class
objects, and don't have any corner cases on method dispatch.
In the expected use of show(), namely automatically showing an object,
additional arguments don't make sense (since there's no chance to
provide them).

If the only problem in my use of print is that it's not called
automatically, then perhaps I should leave it as is and define a show
method that invokes print().  That seems to be the pattern in the
example you provided below.
Yes, that's it.  And that all makes sense.
Ah, so the lack of a print method in the promptClass output is a bug?

Ross
#
Ross Boylan <ross at biostat.ucsf.edu> writes:
I believe you _must_ use the same arguments.  When calling
setMethod("foo", ...) where foo is a function, but not a
standardGeneric, the function foo becomes the default for a newly
created generic and the signature for the new generic is taken from
the formal arguments of the original function foo.  Clear as mud?

If you want to use different argument names, then you want your own
generic.  That is ok, IMO.  That is why we have name spaces.  It only
makes sense to set a method on an existing function (plain function,
S3, or S4 generic) if it "means" the same thing [this is obviously
fuzzy].  IMO, there is no benefit of sharing a generic or having a
default function on a generic that doesn't make sense.

Here's an example of creating a generic out of the head function:

    > setClass("FOO", representation(x="character"))
    [1] "FOO"
    > getGeneric("head")
    NULL
    > args(head)
    function (x, ...) 
    NULL
    > setMethod("head", "FOO", function(x, ...) x at x[1:10])
    Creating a new generic function for "head" in ".GlobalEnv"
    [1] "head"
    > getGeneric("head")
    standardGeneric for "head" defined from package "utils"
    
    function (x, ...) 
    standardGeneric("head")
    <environment: 0x1d56600>
    Methods may be defined for arguments: x 

Now head() is a good example because it means something like "show me
the first part of something".  But your application might want a
function named head that meant "what's attached to the neck of an
animal".  In that case, a new generic within your package's name space
seems advisable.

+ seth