Dear Developers,
In current implementation of validity method, objects are first coerced to
superclass (slots are striped). Thus, it is not possible to write validity
method which would perform some checks on children slots.
Say, I want to check if number of slots in a class is equal to "n":
setClass("A", representation(a="numeric", n="integer"),
prototype=list(a=12, n=1L),
validity=function(object){
if(length(slotNames(object))!=object at n+1) paste("Number of
slots must be ", object at n)
else TRUE
})
setClass("B", representation(b="numeric"), contains="A",
prototype=list(a=12, b=14, n=2L))
new("B", a=11, b=33)
Error in validObject(.Object) :
invalid class "B" object: Number of slots must be 2
Error, because an object of class "A" is passed to validObject with one
slot "b" removed and n=2.
Is were a work around for this, or I am just doomed to write the same
validity method for each children class?
Many thanks,
Vitalie.
--
S4: inheritance of validity methods?
6 messages · Martin Morgan, Vitalie S.
"Vitalie S." <vitosmail at rambler.ru> writes:
Dear Developers,
In current implementation of validity method, objects are first
coerced to superclass (slots are striped). Thus, it is not possible
to write validity method which would perform some checks on children
slots.
Say, I want to check if number of slots in a class is equal to "n":
setClass("A", representation(a="numeric", n="integer"),
prototype=list(a=12, n=1L),
validity=function(object){
if(length(slotNames(object))!=object at n+1) paste("Number
of slots must be ", object at n)
else TRUE
})
setClass("B", representation(b="numeric"), contains="A",
prototype=list(a=12, b=14, n=2L))
new("B", a=11, b=33)
Error in validObject(.Object) :
invalid class "B" object: Number of slots must be 2
Error, because an object of class "A" is passed to validObject with
one slot "b" removed and n=2.
Is were a work around for this, or I am just doomed to write the same
validity method for each children class?
I think you're doomed, but checking the number of slots in an instance seems like a very unusual validity method -- isn't this the job of the methods package, not your implementation of particular classes? Martin
Many thanks, Vitalie.
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
On Tue, 18 Aug 2009 15:42:48 +0200, Martin Morgan <mtmorgan at fhcrc.org> wrote:
"Vitalie S." <vitosmail at rambler.ru> writes:
Dear Developers,
In current implementation of validity method, objects are first
coerced to superclass (slots are striped). Thus, it is not possible
to write validity method which would perform some checks on children
slots.
Say, I want to check if number of slots in a class is equal to "n":
setClass("A", representation(a="numeric", n="integer"),
prototype=list(a=12, n=1L),
validity=function(object){
if(length(slotNames(object))!=object at n+1) paste("Number
of slots must be ", object at n)
else TRUE
})
setClass("B", representation(b="numeric"), contains="A",
prototype=list(a=12, b=14, n=2L))
new("B", a=11, b=33)
Error in validObject(.Object) :
invalid class "B" object: Number of slots must be 2
Error, because an object of class "A" is passed to validObject with
one slot "b" removed and n=2.
Is were a work around for this, or I am just doomed to write the same
validity method for each children class?
I think you're doomed, but checking the number of slots in an instance seems like a very unusual validity method -- isn't this the job of the methods package, not your implementation of particular classes?
That was a toy example. In fact that I need is to ensure that some specific slots have the same length. Names of these slots are kept in separate slot (@atributes). I think it's not an insane idea to require a predefined relationship to hold between "future" children's slots. To be honest, I find it hard to think of a compelling reason to convert the object before testing the validity. What can possibly go wrong if children slots are kept in place? Vitalie.
Martin
Many thanks, Vitalie.
--
Vitalie S. wrote:
On Tue, 18 Aug 2009 15:42:48 +0200, Martin Morgan <mtmorgan at fhcrc.org> wrote:
"Vitalie S." <vitosmail at rambler.ru> writes:
Dear Developers,
In current implementation of validity method, objects are first
coerced to superclass (slots are striped). Thus, it is not possible
to write validity method which would perform some checks on children
slots.
Say, I want to check if number of slots in a class is equal to "n":
setClass("A", representation(a="numeric", n="integer"),
prototype=list(a=12, n=1L),
validity=function(object){
if(length(slotNames(object))!=object at n+1) paste("Number
of slots must be ", object at n)
else TRUE
})
setClass("B", representation(b="numeric"), contains="A",
prototype=list(a=12, b=14, n=2L))
new("B", a=11, b=33)
Error in validObject(.Object) :
invalid class "B" object: Number of slots must be 2
Error, because an object of class "A" is passed to validObject with
one slot "b" removed and n=2.
Is were a work around for this, or I am just doomed to write the same
validity method for each children class?
I think you're doomed, but checking the number of slots in an instance seems like a very unusual validity method -- isn't this the job of the methods package, not your implementation of particular classes?
Perhaps I was too quick in saying you were doomed. First, if A is
VIRTUAL then the class seen by its validity method is, as implied by ?as,
the object will be from the closest actual class, in particular the
original object, if that class extends the virtual class directly
so
.validity <- function(object) {
cat(class(object), "\n")
TRUE
}
setClass("A",
representation(a="numeric", n="integer", "VIRTUAL"),
validity=.validity)
setClass("B", representation(b="numeric"), contains="A")
leads to
res <- new("B", b=1)
B
This seems to require some careful thought about how derived classes are
to behave.
Another approach, probably obvious and which does not really bring
anything 'automatic', is to define a helper function that can be called
in each validity method, or itself used as the validity method.
.validity <- function(object) {
# TRUE or message
}
setClass("A", representation(a="numeric", n="integer"))
setClass("B", representation(b="numeric"), contains="A",
validity=.validity)
setClass("C", representation(c="numeric"), contains="A",
validity=function(object) .validity(object))
one might make .validity generic, so that one gets inheritance &
dispatch in the validity methods.
setGeneric(".validity")
setClass("A", representation(a="numeric", n="integer"))
setClass("B", representation("A", b="numeric"), validity=.validity)
setMethod(.validity, "A", function(object) {
cat(class(object), "\n")
TRUE
})
That was a toy example. In fact that I need is to ensure that some specific slots have the same length. Names of these slots are kept in separate slot (@atributes). I think it's not an insane idea to require a predefined relationship to hold between "future" children's slots. To be honest, I find it hard to think of a compelling reason to convert the object before testing the validity. What can possibly go wrong if children slots are kept in place?
I guess this is reflected in 'setIs' / 'setAs', which allow for derived classes to have and interpret their representation in a way that is completely different from parent classes. Martin
Vitalie.
Martin
Many thanks, Vitalie.
On Tue, 18 Aug 2009 18:58:27 +0200, Martin Morgan <mtmorgan at fhcrc.org> wrote:
Vitalie S. wrote:
On Tue, 18 Aug 2009 15:42:48 +0200, Martin Morgan <mtmorgan at fhcrc.org> wrote:
"Vitalie S." <vitosmail at rambler.ru> writes:
Dear Developers,
In current implementation of validity method, objects are first
coerced to superclass (slots are striped). Thus, it is not possible
to write validity method which would perform some checks on children
slots.
Say, I want to check if number of slots in a class is equal to "n":
setClass("A", representation(a="numeric", n="integer"),
prototype=list(a=12, n=1L),
validity=function(object){
if(length(slotNames(object))!=object at n+1) paste("Number
of slots must be ", object at n)
else TRUE
})
setClass("B", representation(b="numeric"), contains="A",
prototype=list(a=12, b=14, n=2L))
new("B", a=11, b=33)
Error in validObject(.Object) :
invalid class "B" object: Number of slots must be 2
Error, because an object of class "A" is passed to validObject with
one slot "b" removed and n=2.
Is were a work around for this, or I am just doomed to write the same
validity method for each children class?
I think you're doomed, but checking the number of slots in an instance seems like a very unusual validity method -- isn't this the job of the methods package, not your implementation of particular classes?
Perhaps I was too quick in saying you were doomed. First, if A is
VIRTUAL then the class seen by its validity method is, as implied by ?as,
the object will be from the closest actual class, in particular the
original object, if that class extends the virtual class directly
so
.validity <- function(object) {
cat(class(object), "\n")
TRUE
}
setClass("A",
representation(a="numeric", n="integer", "VIRTUAL"),
validity=.validity)
setClass("B", representation(b="numeric"), contains="A")
leads to
res <- new("B", b=1)
B This seems to require some careful thought about how derived classes are to behave.
yes, indeed. At first glance seems as a nice trick, but:
setClass("C", representation(c="numeric"), contains="B")
[1] "C"
res <- new("C", c=1)
B yah... same story at a different level:(.
Another approach, probably obvious and which does not really bring
anything 'automatic', is to define a helper function that can be called
in each validity method, or itself used as the validity method.
.validity <- function(object) {
# TRUE or message
}
setClass("A", representation(a="numeric", n="integer"))
setClass("B", representation(b="numeric"), contains="A",
validity=.validity)
setClass("C", representation(c="numeric"), contains="A",
validity=function(object) .validity(object))
one might make .validity generic, so that one gets inheritance &
dispatch in the validity methods.
setGeneric(".validity")
setClass("A", representation(a="numeric", n="integer"))
setClass("B", representation("A", b="numeric"), validity=.validity)
setMethod(.validity, "A", function(object) {
cat(class(object), "\n")
TRUE
})
This one has similar problem:
setGeneric(".validity", useAsDefault=function(object) {
cat(class(object), "\n")
TRUE
})
setClass("A", representation(a="numeric", n="integer"),
validity=.validity)
setClass("B", representation("A", b="numeric"), validity=.validity)
setClass("C", representation("B", c="numeric"), validity=.validity)
tb <- new("B", b=2)
A B
tc <- new("C", c=3)
A
B
C
?validObject says:
Validity testing takes place *bottom up*(...)for each of the classes
that this
class extends (the ?superclasses?), the explicit validity method
of that class is called, if one exists. Finally, the validity
method of 'object''s class is called, if there is one.
This is ectly what happens above when A,B,C is printed.
It seams like there is no way to implement a general function which would
be called for validation of each children class. Moreover, if I attempt to
rewrite manually the validity method for each subclass taht would also not
work, because validity is tested "bottom up". validObject for A is called
anyway - and is obviously not valid:
setClass("A", representation(a="numeric", n="integer"),
validity=function(object) length(slotNames(object))==object at n+1
)
setClass("B", representation(b="numeric"), contains="A",
validity=function(object) length(slotNames(object))==object at n+1
)
tb <- new("B", b=2, n=2L)
Error in validObject(.Object) : invalid class "B" object: FALSE So there is no way to validate at all!!!
That was a toy example. In fact that I need is to ensure that some specific slots have the same length. Names of these slots are kept in separate slot (@atributes). I think it's not an insane idea to require a predefined relationship to hold between "future" children's slots. To be honest, I find it hard to think of a compelling reason to convert the object before testing the validity. What can possibly go wrong if children slots are kept in place?
I guess this is reflected in 'setIs' / 'setAs', which allow for derived classes to have and interpret their representation in a way that is completely different from parent classes. Martin
Vitalie.
Martin
Many thanks, Vitalie.
--
1 day later
With the help of Martin Morgan here is the solution for the above question.
First create VIRTUAL class with all the test for children classes:
setClass("TESTS", validity=function(object) {
cat("Validity here:", class(object), "\n")
TRUE
})
.simple <- function(object) {
cat("No validity -", class(object), "\n")
TRUE
}
setClass("A", representation(a="numeric", n="integer"),
validity=.simple)
setClass("B", representation("A", b="numeric"),
validity=.simple)
setClass("C", representation("B", c="numeric"),
validity=.simple)
Set each children to inherit from TESTS class:
setIs("B", "TESTS")
setIs("C", "TESTS")
Now we have exactly what we wanted, validity tests are executed only once:
res <- new("B", b=1)
Validity here: B No validity - A No validity - B
res <- new("C", c=1)
No validity - A Validity here: C No validity - B No validity - C The order in which validity is called above is somewhat counter intuitive. For class "C" complete hierarchy is this:
superClassDepth(getClass("C"))$label
[1] "B" "TESTS" "A" "TESTS" "TESTS"
so if validity would start bottomup then TEST should be the first to
follow. And vector of unique superclasses would be
c("B", "A", "TEST").
Instead validity uses the vector of unique labels constructed *TOPDOWN* :
getAllSuperClasses(getClass("C"))
[1] "B" "TESTS" "A" Apparently this is an inconsistency in implementation, isn't it? Vitalie. On Mon, 17 Aug 2009 20:06:44 +0200, Vitalie S. <vitosmail at rambler.ru> wrote:
Dear Developers,
In current implementation of validity method, objects are first coerced
to superclass (slots are striped). Thus, it is not possible to write
validity method which would perform some checks on children slots.
Say, I want to check if number of slots in a class is equal to "n":
setClass("A", representation(a="numeric", n="integer"),
prototype=list(a=12, n=1L),
validity=function(object){
if(length(slotNames(object))!=object at n+1) paste("Number of
slots must be ", object at n)
else TRUE
})
setClass("B", representation(b="numeric"), contains="A",
prototype=list(a=12, b=14, n=2L))
new("B", a=11, b=33)
Error in validObject(.Object) :
invalid class "B" object: Number of slots must be 2
Error, because an object of class "A" is passed to validObject with one
slot "b" removed and n=2.
Is were a work around for this, or I am just doomed to write the same
validity method for each children class?
Many thanks,
Vitalie.
--
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
--