[R] inheritence in S4
cgenolin at u-paris10.fr
cgenolin at u-paris10.fr
Thu Mar 27 10:41:14 CET 2008
Sorry to come back on callNextMethod, I am still not very confident about it.
Consideres the following (there is a lot of code, but very simple with
almost only some cat) :
------------------
setClass("A",representation(a="numeric"))
setValidity("A",function(object){cat(" ***** Valid A *****\n");TRUE})
setMethod("initialize","A",function(.Object){
cat("****** Init A ******\n")
.Object <- callNextMethod()
return(.Object)
})
setClass("B",representation(b="numeric"),contains="A")
setValidity("B",function(object){cat(" *** Valid B ***\n");TRUE})
setMethod("initialize","B",function(.Object){
cat(" **** Init B ****\n")
.Object <- callNextMethod()
return(.Object)
})
new("B",a=3,b=2)
######## Result ########
# **** Init B ****
# ****** Init A ******
# ***** Valid A *****
# *** Valid B ***
# An object of class "B"
# Slot "b":
# [1] 2
# # Slot "a":
# [1] 3
--------------------
new("B") will go trought
- initialize B that will call the nextMethod that is :
- initialize A that will call the nextMethod that is :
- initialize ANY call validObject A.
This would be perfect... But there is also a call to validObject B.
Where does it come from ?
This is anoying because :
> In an object-oriented sense, initialize,B-method should really just
> deal with it's own slots; it shouldn't have to 'know' about either
> classes that it extends (A) or classes that extend it.
I completly agree with that. But if the author of A change its code :
---------------------
setClass("A",representation(a="numeric"))
setValidity("A",function(object){cat(" ***** Valid A *****\n");TRUE})
setMethod("initialize","A",function(.Object){
cat("****** Init A ******\n")
.Object at a <- 10
return(.Object)
})
setClass("B",representation(b="numeric"),contains="A")
setValidity("B",function(object){cat(" *** Valid B ***\n");TRUE})
setMethod("initialize","B",function(.Object){
cat(" **** Init B ****\n")
.Object <- callNextMethod()
return(.Object)
})
new("B",a=3,b=2)
######## Result ########
# **** Init B ****
# ****** Init A ******
# An object of class "B"
# Slot "b":
# numeric(0)
#
# Slot "a":
# [1] 10
---------------------
Then validObject of B is no longer call, and B is no longueur correctly set...
So if A is changed by its author, the comportement of B is change as well...
Anoying, isn't it ?
But I agree with the
> "initialize,B-method should really just deal with it's own slots;"
So may be something like :
---------------------
setClass("A",representation(a="numeric"))
setValidity("A",function(object){cat(" ***** Valid A *****\n");TRUE})
setMethod("initialize","A",function(.Object){
cat("****** Init A ******\n")
# .Object at a <- 10
.Object <- callNextMethod()
return(.Object)
})
setClass("B",representation(b="numeric"),contains="A")
setValidity("B",function(object){cat(" *** Valid B ***\n");TRUE})
setMethod("initialize","B",function(.Object,a,b){
cat(" **** Init B ****\n")
as(.Object,"A") <- new("A",a=a)
.Object at b <- b
return(.Object)
})
new("B",a=3,b=2)
######## Result ########
# **** Init B ****
# ****** Init A ******
# An object of class "B"
# Slot "b":
# [1] 2
#
# Slot "a":
# [1] 10
---------------------
The call to validObject of B is no longer dependent of the A code.
Christophe
> cgenolin at u-paris10.fr wrote:
>> Hi Martin
>>
>> I am re reading all the mail we exchange with new eyes because of
>> all the thing I learn in the past few weeks. That very interesting
>> and some new question occurs...
>>
>> ***********************************
>> Once, you speak about callGeneric :
>>
>> setClass("A", representation(x="numeric"))
>> setClass("C", contains=c("A"))
>>
>> setMethod("show", "A", function(object) cat("A\n"))
>> setMethod("show", "C", function(object) {
>> callGeneric(as(object, "A"))
>> cat("C\n")
>> })
>>
>> new("C")
>>
>> Considere the following definition (that you more or less teach me
>> with your yesterday remarques...) :
>>
>> setMethod("show", "C", function(object) {
>> callNextMethod()
>> cat("C\n")
>> })
>>
>> In this case, is there any difference between the former and the latter ?
>> Which one would you use ?
>
> callNextMethod is the right thing to do for this case. callGeneric is
> useful in a very specific case -- when dispatching from within a
> so-called 'group generic' function. But this is an advanced topic.
>
>> (I get that in more complicate case, for example if
>> setClass("C", contains=c("A","B")), it might be more complicate to
>> use the latter, right ?)
>
> The right thing to do in this case is to sit down with the rules of
> method dispatch, and figure out what the 'next' method will be. A
> common alternative paradigm is to have a plain function (not visible
> to the user, e.g., not exported in a package name space) that several
> different methods all invoke, after mapping their arguments
> appropriately. The methods provide a kind of structured interface to
> the function, making sure arguments are of the appropriate type, etc.
> The function does the work, confident that the arguments are
> appropriate.
>
>
>> *************************
>> This works :
>>
>> setMethod("initialize","B",
>> function(.Object,..., yValue){
>> callNextMethod(.Object, ..., y=yValue)
>> return(.Object)
>> })
>> new("B",yValue=3)
>>
>> but this does not :
>>
>> setMethod("initialize","B",
>> function(.Object, yValue){
>> callNextMethod(.Object, y=yValue)
>> return(.Object)
>> })
>> new("B",yValue=3)
>>
>> Why ?
>> Is there any help page about ... ?
>
> Both 'work' in the sense that an object is returned (by the way, no
> need to use 'return' explicitly). And actually the examples on some
> of the man pages do not include '...', so this is really my opinion
> rather than the 'right' way to do things.
>
> In an object-oriented sense, initialize,B-method should really just
> deal with it's own slots; it shouldn't have to 'know' about either
> classes that it extends (A) or classes that extend it. And it
> shouldn't do work that inherited methods (i.e.,
> initialize,ANY-method) do. In the second form above, without the ...,
> there is no way for the initialize,A-method to see arguments that
> might be relevant to it (e.g., values to be used to initialize its
> slots). So initialize,B-method would have to do all the work of
> initializing A. This is not good design.
>
>
>> **************************
>> showMethods gives the list of all the method. Is there a way to see
>> all the method for a specific signature IN THE ORDER they will be
>> call by callNextMethod ?
>> If ANY <- D <- E, a method that will gives :
>>
>> Function "initialize":
>> .Object = "E"
>> .Object = "D"
>> .Object = "ANY"
>
> There is, but I have never been able to figure it out in detail or to
> feel confident that I was using functions that were meant to be used
> for this purpose by the user (as opposed to by the methods package).
>
> John Chambers posted recently to the R-devel mailing list about
> changes to the internal representation of methods and classes.
>
> https://stat.ethz.ch/pipermail/r-devel/2008-March/048729.html
>
> I have not explored the new functions Dr. Chambers mentions; to use
> them requires the 'devel' version of R, not 2.6.2. Any questions they
> generate should definitely be addressed to the R-devel mailing list.
>
> Best,
>
> Martin
>
>> Thanks for your help
>> And happy easter eggs !
>>
>> Christophe
>>
>>
>> ----------------------------------------------------------------
>> Ce message a ete envoye par IMP, grace a l'Universite Paris 10 Nanterre
>>
>>
>>
>
>
----------------------------------------------------------------
Ce message a ete envoye par IMP, grace a l'Universite Paris 10 Nanterre
More information about the R-help
mailing list