Re: [Rd] Problem using callNextMethod() in S4

2007-03-05 Thread cstrato
Dear Martin

Thank you for this extensive explanation!
I hope that I have understood it, but I will realize it as I proceed with my
package. Now, my example works as I have expected it to work, great!

One problem that I face is lack of extensive explanation of S4 classes,
especially with respect to inheritance.  (Neither the tutorial "S4 Classes
in 15 pages" nor the book "S Programming" are really helpful.) Most
of the time I study the code in BioBase and affy, since there are not
many packages available using S4 and inheritance.
Your explanation below is a good example, how a good S4 tutorial
should cover these issues.

Best regards
Christian

Martin Morgan wrote:
> In this method...
>
> setMethod("initialize", "baseClass",
>function(.Object, ...) {
> print("---initialize:baseClass---")
> #.Object <- callNextMethod();
>   strg <- [EMAIL PROTECTED];
> print(paste("base:strg = ", strg))
>   if (strg == "") {
>  [EMAIL PROTECTED] <- as.character(getwd());
>   }#if
>   if (substr(strg, nchar(strg), nchar(strg)) == "/") {
>  [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
>   }#if
> print(paste("base:mydir = ", [EMAIL PROTECTED]))
> .Object <- callNextMethod();
> .Object;
>}
> )#initialize
>
> the argument '...' includes the argument mydir="". Later, when you
> .Object <- callNextMethod(), it invokes the 'next' method with the
> same argument, i.e., with mydir="". This causes the 'mydir' slot to be
> initialized with "", triggering the validity error. You can see this
> more clearly in the following, where the provided argument x=10:1
> overrides the assignment in initialize:
>
>   
>> setClass("A", representation=representation(x="numeric"))
>> 
> [1] "A"
>   
>> setMethod("initialize", "A",
>> 
> +   function(.Object, ...) {
> +   [EMAIL PROTECTED] <- 1:10
> +   callNextMethod()
> +   })
> [1] "initialize"
>
>   
>> new("A", x=10:1)
>> 
> An object of class "A"
> Slot "x":
>  [1] 10  9  8  7  6  5  4  3  2  1
>
> One solution is to name any arguments you're going to manipulate in
> the initialize method, and then make sure the correct arguments are
> passed to callNextMethod. You'll probably want to provide a sensible
> default argument to mydir, so that user doesn't have to do anything
> clever (like remember to pass "") to get the default behavior. Here's
> what I end up with:
>
> setMethod("initialize", "baseClass",
>   function(.Object, mydir=as.character(getwd()), ...) {
>   if (substr(mydir, nchar(mydir), nchar(mydir)) == "/") {
>   mydir <- substr(mydir, 0, nchar(mydir)-1)
>   }
>   callNextMethod(.Object, mydir=mydir, ...);
>   })
>
> setMethod("initialize", "derivedClass",
>   function(.Object, mytitle="MyTitle", ...) {
>   callNextMethod(.Object, mytitle=mytitle, ...)
>   })
>
> Another solution is to follow the convention where callNextMethod()
> comes first (constructing a valid object!), and your initialize method
> then fills in slots with the appropriate values.
>
> One interesting part of your example is that new('derivedClass') does
> NOT cause a validity error, even though the object is invalid
> ('myname' is ""; also, none of your validity method messages are
> printed)! Apparently, the assumption is that you (the programmer, as
> opposed to the user) are not going to create an invalid object by
> default.
>
> Also, take a look at the initialize method that R has constructed for
> derivedClass:
>
>   
>> getMethod("initialize", "derivedClass")
>> 
> Method Definition:
>
> function (.Object, ...) 
> {
> .local <- function (.Object, mytitle = "MyTitle", ...) 
> {
> callNextMethod(.Object, mytitle = mytitle, ...)
> }
> .local(.Object, ...)
> }
>
> Signatures:
> .Object   
> target  "derivedClass"
> defined "derivedClass"
>
> Notice how the function is defined in terms of .Object and  The
> named arguments not present in the generic signature (i.e., 'mytitle')
> are 'hidden' in the .local function definition. By the time
> callNextMethod() has been evaluated, '...' does NOT include
> 'mytitle'. I think this is why you must explicitly include any named
> arguments you want to pass to callNextMethod -- the default is to
> callNextMethod with the generic signature, but with symbols (.Object,
> ...) taking their current value. Here's a simpler illustration:
>
> setClass("A", representation=representation(x="numeric"))
> setMethod("initialize", "A",
>   function(.Object, x, ...) callNextMethod())
>
> This leads to the perhaps unexpected outcome
>
>   
>> new("A", x=10:1)
>> 
> An object of class "A"
> Slot "x":
> numeric(0)
>
> I say unexpected because, if there was no initialize method, or if the
> initialize method were written without 'x' in the signature, then the
> argument 'x' would be used to fill the slot:x.
>
> Here's the soluti

Re: [Rd] Problem using callNextMethod() in S4

2007-03-04 Thread Martin Morgan
In this method...

setMethod("initialize", "baseClass",
   function(.Object, ...) {
print("---initialize:baseClass---")
#.Object <- callNextMethod();
  strg <- [EMAIL PROTECTED];
print(paste("base:strg = ", strg))
  if (strg == "") {
 [EMAIL PROTECTED] <- as.character(getwd());
  }#if
  if (substr(strg, nchar(strg), nchar(strg)) == "/") {
 [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
  }#if
print(paste("base:mydir = ", [EMAIL PROTECTED]))
.Object <- callNextMethod();
.Object;
   }
)#initialize

the argument '...' includes the argument mydir="". Later, when you
.Object <- callNextMethod(), it invokes the 'next' method with the
same argument, i.e., with mydir="". This causes the 'mydir' slot to be
initialized with "", triggering the validity error. You can see this
more clearly in the following, where the provided argument x=10:1
overrides the assignment in initialize:

> setClass("A", representation=representation(x="numeric"))
[1] "A"
> setMethod("initialize", "A",
+   function(.Object, ...) {
+   [EMAIL PROTECTED] <- 1:10
+   callNextMethod()
+   })
[1] "initialize"

> new("A", x=10:1)
An object of class "A"
Slot "x":
 [1] 10  9  8  7  6  5  4  3  2  1

One solution is to name any arguments you're going to manipulate in
the initialize method, and then make sure the correct arguments are
passed to callNextMethod. You'll probably want to provide a sensible
default argument to mydir, so that user doesn't have to do anything
clever (like remember to pass "") to get the default behavior. Here's
what I end up with:

setMethod("initialize", "baseClass",
  function(.Object, mydir=as.character(getwd()), ...) {
  if (substr(mydir, nchar(mydir), nchar(mydir)) == "/") {
  mydir <- substr(mydir, 0, nchar(mydir)-1)
  }
  callNextMethod(.Object, mydir=mydir, ...);
  })

setMethod("initialize", "derivedClass",
  function(.Object, mytitle="MyTitle", ...) {
  callNextMethod(.Object, mytitle=mytitle, ...)
  })

Another solution is to follow the convention where callNextMethod()
comes first (constructing a valid object!), and your initialize method
then fills in slots with the appropriate values.

One interesting part of your example is that new('derivedClass') does
NOT cause a validity error, even though the object is invalid
('myname' is ""; also, none of your validity method messages are
printed)! Apparently, the assumption is that you (the programmer, as
opposed to the user) are not going to create an invalid object by
default.

Also, take a look at the initialize method that R has constructed for
derivedClass:

> getMethod("initialize", "derivedClass")
Method Definition:

function (.Object, ...) 
{
.local <- function (.Object, mytitle = "MyTitle", ...) 
{
callNextMethod(.Object, mytitle = mytitle, ...)
}
.local(.Object, ...)
}

Signatures:
.Object   
target  "derivedClass"
defined "derivedClass"

Notice how the function is defined in terms of .Object and  The
named arguments not present in the generic signature (i.e., 'mytitle')
are 'hidden' in the .local function definition. By the time
callNextMethod() has been evaluated, '...' does NOT include
'mytitle'. I think this is why you must explicitly include any named
arguments you want to pass to callNextMethod -- the default is to
callNextMethod with the generic signature, but with symbols (.Object,
...) taking their current value. Here's a simpler illustration:

setClass("A", representation=representation(x="numeric"))
setMethod("initialize", "A",
  function(.Object, x, ...) callNextMethod())

This leads to the perhaps unexpected outcome

> new("A", x=10:1)
An object of class "A"
Slot "x":
numeric(0)

I say unexpected because, if there was no initialize method, or if the
initialize method were written without 'x' in the signature, then the
argument 'x' would be used to fill the slot:x.

Here's the solution like that for baseClass, above:

setMethod("initialize", "A",
  function(.Object, x, ...)
  callNextMethod(.Object, x=x, ...))

...which leads to

> new("A", x=10:1)
An object of class "A"
Slot "x":
 [1] 10  9  8  7  6  5  4  3  2  1

Hope that helps,

Martin

cstrato <[EMAIL PROTECTED]> writes:

> Dear Seth
>
> Thank you for your comments. Please see my comments and at the end my 
> corrected code and output.
> Sorrowly, the problem remains the same.
>
> Seth Falcon wrote:
>> cstrato <[EMAIL PROTECTED]> writes:
>>
>>   
>>> Dear all,
>>>
>>> Maybe, I am doing something wrong, but using R-2.5.0 on my Intel-Mac, I 
>>> have problems
>>> using function callNextMethod() in method initialize.
>>>
>>> I am loading the following code as file "testS4.R":
>>> 
>>
>> I don't think this is the code in the same state as that which you ran
>> the examples.  Did you add/remove some comment lines perhaps?
>>
>> After copy/pasti

Re: [Rd] Problem using callNextMethod() in S4

2007-03-04 Thread cstrato
Dear Seth

Thank you for your comments. Please see my comments and at the end my 
corrected code and output.
Sorrowly, the problem remains the same.

Seth Falcon wrote:
> cstrato <[EMAIL PROTECTED]> writes:
>
>   
>> Dear all,
>>
>> Maybe, I am doing something wrong, but using R-2.5.0 on my Intel-Mac, I 
>> have problems
>> using function callNextMethod() in method initialize.
>>
>> I am loading the following code as file "testS4.R":
>> 
>
> I don't think this is the code in the same state as that which you ran
> the examples.  Did you add/remove some comment lines perhaps?
>
> After copy/pasting the code you posted, I get:
>
> > tmp<-new("derivedClass")
> [1] "---initialize:derivedClass---"
> [1] "mytitle =  MyTitle"
> > tmp<-new("derivedClass",myname="testname",mytitle="testitle")
> [1] "---initialize:derivedClass---"
> [1] "mytitle =  MyTitle"
>
>   
I am sorry, you are correct, I have commented out ".Object <- 
callNextMethod()" in method
initialize for derivedClass afterwards.
>> setValidity("baseClass",
>>function(object) {
>> print("---setValidity:baseClass---")
>>   strg <- [EMAIL PROTECTED];
>>   if (!(is(strg, "character") && nchar(strg) > 0)) {
>>  warning(paste(sQuote("myname"), "is missing"));
>>   }#if
>> print(paste("myname = ",[EMAIL PROTECTED]))
>>   strg <- [EMAIL PROTECTED];
>>   if (!(is(strg, "character") && file.exists(strg))) {
>>  warning(paste(sQuote("mydir"), "is not a system directory"));
>>   }#if
>>   if (substr(strg, nchar(strg), nchar(strg)) == "/") {
>>  [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
>>   }#if
>> print(paste("mydir = ",[EMAIL PROTECTED]))
>>}
>> )#setValidity
>> 
>
> Your validity function isn't valid :-P It should either return TRUE or
> return a character vector describing what isn't valid about the
> object.  Don't call warning() or print().
>   
Please see my corrected code where I use "validMsg()" from BioBase.
> Also, you don't need those ';'
>
> And finally, you are operating on a _copy_ in the validity method
> (just like everywhere else) and so this
>
>   
>>   if (substr(strg, nchar(strg), nchar(strg)) == "/") {
>>  [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
>>   }#if
>> 
>
> will not have any effect on the instance passed in.  It is an odd
> thing to do in a validity method.
>   
You are right, I moved this code to method "initialize".
> + seth
>
>   
Here is my new code "testS4.R" (as used in the output):

setClass("baseClass",
   representation(myname = "character",
  mydir  = "character",
  "VIRTUAL"),
   prototype(myname = "",
 mydir  = "")
)#baseClass

setClass("derivedClass",
   representation(mytitle = "character"),
   contains=c("baseClass"),
   prototype(mytitle = "")
)#derivedClass

# taken from package BioBase: tools.R
validMsg <- function(msg, result) {
   if (is.character(result)) {
  append(msg, result);
   } else {
  msg;
   }#if
}

setMethod("initialize", "baseClass",
   function(.Object, ...) {
print("---initialize:baseClass---")
#.Object <- callNextMethod();
  strg <- [EMAIL PROTECTED];
print(paste("base:strg = ", strg))
  if (strg == "") {
 [EMAIL PROTECTED] <- as.character(getwd());
  }#if
  if (substr(strg, nchar(strg), nchar(strg)) == "/") {
 [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
  }#if
print(paste("base:mydir = ", [EMAIL PROTECTED]))
.Object <- callNextMethod();
.Object;
   }
)#initialize

setValidity("baseClass",
   function(object) {
print("---setValidity:baseClass---")
  msg <- NULL;
  strg <- [EMAIL PROTECTED];
  if (!(is(strg, "character") && nchar(strg) > 0)) {
 msg <- validMsg(msg, paste(sQuote("myname"), "is missing"));
  }#if
print(paste("base:myname = ",[EMAIL PROTECTED]))
  strg <- [EMAIL PROTECTED];
  if (!(is(strg, "character") && file.exists(strg))) {
 msg <- validMsg(msg, paste(sQuote("mydir"), "is not a system 
directory"));
  }#if
print(paste("base:mydir = ",[EMAIL PROTECTED]))
  if (is.null(msg)) TRUE else msg;
   }
)#setValidity

setMethod("initialize", "derivedClass",
   function(.Object, ...) {
print("---initialize:derivedClass---")
#.Object <- callNextMethod();
  if ([EMAIL PROTECTED] == "") {
 [EMAIL PROTECTED] = "MyTitle";
  }#if
print(paste("derived:mytitle = ",[EMAIL PROTECTED]))
.Object <- callNextMethod();
.Object;
   }
)#initialize

setValidity("derivedClass",
   function(object) {
print("---setValidity:derivedClass---")
  msg <- NULL;
  strg <- [EMAIL PROTECTED];
  if (!(is(strg, "character") && nchar(strg) > 0)) {
 msg <- validMsg(msg, paste(sQuote("mytitle"), "is missing"));
  }#if
print(paste("derived:mytitle = ",[EMAIL PROTECTED]))
  if (is.null(msg)) TRUE else msg;
   }
)#setValidity


Here is the new output with the same error:

 > library(metho

Re: [Rd] Problem using callNextMethod() in S4

2007-03-04 Thread Seth Falcon
cstrato <[EMAIL PROTECTED]> writes:

> Dear all,
>
> Maybe, I am doing something wrong, but using R-2.5.0 on my Intel-Mac, I 
> have problems
> using function callNextMethod() in method initialize.
>
> I am loading the following code as file "testS4.R":

I don't think this is the code in the same state as that which you ran
the examples.  Did you add/remove some comment lines perhaps?

After copy/pasting the code you posted, I get:

> tmp<-new("derivedClass")
[1] "---initialize:derivedClass---"
[1] "mytitle =  MyTitle"
> tmp<-new("derivedClass",myname="testname",mytitle="testitle")
[1] "---initialize:derivedClass---"
[1] "mytitle =  MyTitle"

> setValidity("baseClass",
>function(object) {
> print("---setValidity:baseClass---")
>   strg <- [EMAIL PROTECTED];
>   if (!(is(strg, "character") && nchar(strg) > 0)) {
>  warning(paste(sQuote("myname"), "is missing"));
>   }#if
> print(paste("myname = ",[EMAIL PROTECTED]))
>   strg <- [EMAIL PROTECTED];
>   if (!(is(strg, "character") && file.exists(strg))) {
>  warning(paste(sQuote("mydir"), "is not a system directory"));
>   }#if
>   if (substr(strg, nchar(strg), nchar(strg)) == "/") {
>  [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
>   }#if
> print(paste("mydir = ",[EMAIL PROTECTED]))
>}
> )#setValidity

Your validity function isn't valid :-P It should either return TRUE or
return a character vector describing what isn't valid about the
object.  Don't call warning() or print().

Also, you don't need those ';'

And finally, you are operating on a _copy_ in the validity method
(just like everywhere else) and so this

>   if (substr(strg, nchar(strg), nchar(strg)) == "/") {
>  [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
>   }#if

will not have any effect on the instance passed in.  It is an odd
thing to do in a validity method.

+ seth

-- 
Seth Falcon | Computational Biology | Fred Hutchinson Cancer Research Center
http://bioconductor.org

__
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel


[Rd] Problem using callNextMethod() in S4

2007-03-04 Thread cstrato
Dear all,

Maybe, I am doing something wrong, but using R-2.5.0 on my Intel-Mac, I 
have problems
using function callNextMethod() in method initialize.

I am loading the following code as file "testS4.R":

setClass("baseClass",
   representation(myname = "character",
  mydir  = "character",
  "VIRTUAL"),
   prototype(myname = "",
 mydir  = "")
)#baseClass

setClass("derivedClass",
   representation(mytitle = "character"),
   contains=c("baseClass"),
   prototype(mytitle = "")
)#derivedClass


setMethod("initialize", "baseClass",
   function(.Object, ...) {
print("---initialize:baseClass---")
#.Object <- callNextMethod();
  if ([EMAIL PROTECTED] == "") {
 [EMAIL PROTECTED] <- as.character(getwd());
  }#if
print(paste("mydir = ", [EMAIL PROTECTED]))
.Object <- callNextMethod();
.Object;
   }
)#initialize

setValidity("baseClass",
   function(object) {
print("---setValidity:baseClass---")
  strg <- [EMAIL PROTECTED];
  if (!(is(strg, "character") && nchar(strg) > 0)) {
 warning(paste(sQuote("myname"), "is missing"));
  }#if
print(paste("myname = ",[EMAIL PROTECTED]))
  strg <- [EMAIL PROTECTED];
  if (!(is(strg, "character") && file.exists(strg))) {
 warning(paste(sQuote("mydir"), "is not a system directory"));
  }#if
  if (substr(strg, nchar(strg), nchar(strg)) == "/") {
 [EMAIL PROTECTED] <- substr(strg, 0, nchar(strg)-1);
  }#if
print(paste("mydir = ",[EMAIL PROTECTED]))
   }
)#setValidity

setMethod("initialize", "derivedClass",
   function(.Object, ...) {
print("---initialize:derivedClass---")
#.Object <- callNextMethod();
  if ([EMAIL PROTECTED] == "") {
 [EMAIL PROTECTED] = "MyTitle";
  }#if
print(paste("mytitle = ",[EMAIL PROTECTED]))
#.Object <- callNextMethod();
.Object;
   }
)#initialize

setValidity("derivedClass",
   function(object) {
print("---setValidity:derivedClass---")
  strg <- [EMAIL PROTECTED];
  if (!(is(strg, "character") && nchar(strg) > 0)) {
 warning(paste(sQuote("mytitle"), "is missing"));
  }#if
print(paste("mytitle = ",[EMAIL PROTECTED]))
   }
)#setValidity


This is the output of an R session:

 > library(methods)
 > source("testS4.R")
 > tmp<-new("derivedClass")
[1] "---initialize:derivedClass---"
[1] "mytitle =  MyTitle"
[1] "---initialize:baseClass---"
[1] "mydir =  /Volumes/CoreData/CRAN/Workspaces/tests"
 >
 > tmp<-new("derivedClass",myname="testname",mytitle="testitle")
[1] "---initialize:derivedClass---"
[1] "mytitle =  MyTitle"
[1] "---initialize:baseClass---"
[1] "mydir =  /Volumes/CoreData/CRAN/Workspaces/tests"
[1] "---setValidity:baseClass---"
[1] "myname =  testname"
[1] "mydir =  /Volumes/CoreData/CRAN/Workspaces/tests"
Error in validObject(.Object) : invalid class "derivedClass" object: 
mydir =  /Volumes/CoreData/CRAN/Workspaces/tests
 >
 > tmp<-new("derivedClass",myname="testname",mydir="",mytitle="testitle")
[1] "---initialize:derivedClass---"
[1] "mytitle =  MyTitle"
[1] "---initialize:baseClass---"
[1] "mydir =  /Volumes/CoreData/CRAN/Workspaces/tests"
[1] "---setValidity:baseClass---"
[1] "myname =  testname"
[1] "mydir =  "
Error in validObject(.Object) : invalid class "derivedClass" object: 
mydir = 
In addition: Warning message:
'mydir' is not a system directory in: validityMethod(as(object, 
superClass))
 >

Can someone tell me why mydir gives an error although it is defined 
correctly?

Thank you in advance
Best regards
Christian
_._._._._._._._._._._._._._._._
C.h.i.s.t.i.a.n S.t.r.a.t.o.w.a
V.i.e.n.n.a   A.u.s.t.r.i.a
_._._._._._._._._._._._._._._._

__
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel