[R] S4: Initialization method called during setClass??

Wacek Kusnierczyk Waclaw.Marcin.Kusnierczyk at idi.ntnu.no
Fri Jun 5 23:17:43 CEST 2009


Martin Morgan wrote:
> Hi Vitalie --
>
> Vitalie S. wrote:
>   
>> Dear UseRs,
>>
>> A simple class inheritance example:
>>
>>     
>>> setClass("test",representation(a="numeric"))
>>> setMethod("initialize","test",
>>>       
>>           function(.Object,x,...){
>>               print("Initialization!!!")
>>               callNextMethod(.Object,a=x,...)
>>           })
>>
>>     
>>> new("test",x=23)
>>>       
>> [1] "Initialization!!!"
>> An object of class "test"
>> Slot "a":
>> [1] 23
>>     
>
> the implicit contract is that the initialize method is written so that
> new("test") works. This contract comes up at several points during class
> definition and instantiation. You've identified one of those points.
>
>   
>>> setClass("test2",contains="test",representation(b="integer"))
>>>       
>> [1] "Initialization!!!"
>> Error in .nextMethod(.Object, a = x, ...) : argument "x" is missing,
>> with no default
>>     
>
> I'm not sure of the details, but part of the class definition is a
> coercion method, and in defining this new() is called, without
> arguments, on the contained classes.
>   

it sounds approximately ok that defining a subclass fails if its
initalizer does not provide a value required by the initializer of the
superclass.  pardon me my ignorance, but here's an obvious question: 
what if i *do* want to extend a class that has an initializer with an
argument without a default?  (it does not seem to be a particularly
extravagant idea, does it?)  i can't define the subclass with setClass,
because it fails, as above, due to a violation of the contract.  but i
can't define an appropriate initializer for the subclass that would
fulfil the contract before i define the subclass itself.  deadlock?

there is another quirk here, or -- in case the above is not a quirk --
there is a quirk here.  imagine i want to implement a class that counts
its instances as they are created, assigning to each a unique identifier
from a contiguous sequence of integers, starting at 1 (this doesn't seem
to be a particularly extravagant idea either):

    setClass('foo', representation(id='integer'))
    setMethod('initialize', 'foo', local({
        id=0L
        function(.Object, ...) {
           id <<- id+1L
           .Object at id = id
           return(.Object) } }))

    foos = replicate(3, new('foo'))
    sapply(foos, slot, 'id')
    # 1 2 3

    setClass('bar', contains='foo', representation(name='character'))
    setMethod('initialize', 'bar', function(.Object, name='bar', ...) {
       .Object at name = name
       callNextMethod() })
    bars = replicate(3, new('bar'))
    sapply(bars, slot, 'id')
    # 6 7 8

what??  why on earth creating a subclass *calls* (twice!) the
superclass's initializer?  is there no other way for checking the
contract without actually calling the initializer, as if an instance
(two!) was created?  suppose one creates a package defining a hierarchy
of classes with something like 'foo' at the top, and a user of the
package loads it and creates a new -- the first -- instance which
suddenly has 17 as the id -- just imagine the curses.

pardon me for the usual harshness, but this is seems irrational, plain
and simple.  i can imagine that this is a well-documented behaviour
(even if that's only in fine print), but the motivation...?


btw.,

    sapply(foos, `@`, 'id')
    # Error in lapply(X, FUN, ...) :
    #  no slot of name "..." for this object of class "foo"

even though
   
    `@`(foos[[1]], 'id')
    # 1

    `slot`(foos[[1]], 'id')
    # 1

and sapply works fine with slot.  what's wrong here?

deeply confused,
vQ




More information about the R-help mailing list