Re: [Rd] Problem using callNextMethod() in S4
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
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
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
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
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