Vitalie S.
2009-Jun-05 19:44 UTC
[R] S4: When is validObject issued? (or why S4 is killing me:( ..
Dear UseRs, Does anyone know when exactly the validity is checked in S4? Documentation is silent:(. Here is a small example: setClass("test1",representation(a="numeric")) setMethod("initialize","test1", function(.Object,...){ a<-runif(1) ## here slot "a" is initialized ## callNextMethod(.Object,a=a,...) })> new("test1")An object of class "test1" Slot "a": [1] 0.755 #next new subclass is created with an additional slot "b": setClass("test2",contains="test1",representation(b="numeric") ## validity to test a==b ## ,validity=function(object){ if(object at a!=object at b) print("values must be equal!!") else TRUE }) setMethod("initialize","test2", function(.Object,...){ .Object<-callNextMethod(.Object,...) .Object at b<-.Object at a ## here "b" is initialized ## .Object })> new("test2")Error in if (object at a != object at b) print("values must be equal!!") else TRUE : argument is of length zero This could mean only one thing, validity of "test2" is checked before it gets to point when b is initialized (i.e. in callNextMethod). But this is absurd (at leas from my point): callNextMethod constructs object of type "test" and should have nothing to do with validity of "test2". Shouldn't validity be checked only after the object is created? Second my slot "a" is initialized in "test1"'s constructor and "b" must be assigned only after the callNextMethod as in above code. Seemingly unsolvable paradox? How to be? Many thanks.
Martin Morgan
2009-Jun-05 22:33 UTC
[R] S4: When is validObject issued? (or why S4 is killing me:( ..
"Vitalie S." <vitosmail at rambler.ru> writes:> Dear UseRs, > > Does anyone know when exactly the validity is checked in S4? > Documentation is silent:(.It gets called as part of initialize,ANY-method, i.e., at the bottom of your 'callNextMethod'. It is NOT called when there are no arguments in addition to .Object, e.g., in a call like new("test").> Here is a small example: > > setClass("test1",representation(a="numeric")) > setMethod("initialize","test1", > function(.Object,...){ > a<-runif(1) ## here slot "a" is initialized ## > callNextMethod(.Object,a=a,...) }) > >> new("test1") > An object of class "test1" > Slot "a": > [1] 0.755 > > #next new subclass is created with an additional slot "b": > setClass("test2",contains="test1",representation(b="numeric") > ## validity to test a==b ## > ,validity=function(object){ > if(object at a!=object at b) print("values must be equal!!") > else TRUE > }) > > setMethod("initialize","test2", > function(.Object,...){ > .Object<-callNextMethod(.Object,...)validity is checked in the preceeding line> .Object at b<-.Object at a ## here "b" is initialized ## > > .Object > }) > >> new("test2") > Error in if (object at a != object at b) print("values must be equal!!") > else TRUE : argument is of length zero > > This could mean only one thing, validity of "test2" is checked before > it gets to point when b is initialized (i.e. in callNextMethod). But > this is absurd (at leas from my point): callNextMethod constructs > object of type "test" and should have nothing to do with validity of > "test2". Shouldn't validity be checked only after the object is > created?The class of .Object is test2, and initialize should return a (valid) instance of test2, right? So the validity check on test2 has to have been performed before returning from callNextMethod.> Second my slot "a" is initialized in "test1"'s constructor and "b" > must be assigned only after the callNextMethod as in above > code. Seemingly unsolvable paradox? How to be?My own solution would avoid using initialize (because as you're discovering the contract is hard to satisfy in general) and instead use constructors test2 <- function(a=runif(1), b=a, ...) new("test2", a=a, b=b, ...) or similar, which I think helps to provide a bit of an interface between the class and it's implementation. One could think of additional solutions (creating a new 'test' inside 'test2', and not using callNextMethod(), for instance) but this is unlikely to be robust (e.g., when derived classes are relying on initialize,ANY-method to fill in slots with supplied arguments). Martin> > Many thanks. > > ______________________________________________ > R-help at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code.-- Martin Morgan Computational Biology / Fred Hutchinson Cancer Research Center 1100 Fairview Ave. N. PO Box 19024 Seattle, WA 98109 Location: Arnold Building M1 B861 Phone: (206) 667-2793
Vitalie S.
2009-Jun-06 08:05 UTC
[R] S4: When is validObject issued? (or why S4 is killing me:( ..
>> >> setMethod("initialize","test2", >> function(.Object,...){ >> .Object<-callNextMethod(.Object,...) > > validity is checked in the preceeding line > >> .Object at b<-.Object at a ## here "b" is initialized ## >> >> .Object >> }) >> >>> new("test2") >> Error in if (object at a != object at b) print("values must be equal!!") >> else TRUE : argument is of length zero >> >> This could mean only one thing, validity of "test2" is checked before >> it gets to point when b is initialized (i.e. in callNextMethod). But >> this is absurd (at leas from my point): callNextMethod constructs >> object of type "test" and should have nothing to do with validity of >> "test2". Shouldn't validity be checked only after the object is >> created? > > The class of .Object is test2, and initialize should return a (valid) > instance of test2, right? So the validity check on test2 has to have > been performed before returning from callNextMethod. >Oh yes, that is indeed meaningful. So it's default coercion method from test1 to test2 which is called.>> Second my slot "a" is initialized in "test1"'s constructor and "b" >> must be assigned only after the callNextMethod as in above >> code. Seemingly unsolvable paradox? How to be? > > My own solution would avoid using initialize (because as you're > discovering the contract is hard to satisfy in general) and instead > use constructorsI am sure this "contract" and all this initialization/coercion/creation-sequence stuff must be documented somewhere. Though all what I can find is a collection of your responses in R-help archive. Could you please suggest something to read (except R-core code)? For now I am pretty much clarified. Thanks a lot!> test2 <- function(a=runif(1), b=a, ...) new("test2", a=a, b=b, ...) > > or similar, which I think helps to provide a bit of an interface > between the class and it's implementation. > > One could think of additional solutions (creating a new 'test' inside > 'test2', and not using callNextMethod(), for instance) but this is > unlikely to be robust (e.g., when derived classes are relying on > initialize,ANY-method to fill in slots with supplied arguments). > > Martin > >> >> Many thanks. >> >> ______________________________________________ >> R-help at r-project.org mailing list >> https://stat.ethz.ch/mailman/listinfo/r-help >> PLEASE do read the posting guide >> http://www.R-project.org/posting-guide.html >> and provide commented, minimal, self-contained, reproducible code. >