A belated postscript to this thread, because it's a more positive slant 
that I didn't think of until revising some of the related documentation.

As remarked in my previous post, the automatic coerce methods that 
caused Herve problems are generated from the assumption that contains= 
in a class definition implies that the slots are used essentially the 
same way as in the superclass.  That's reflected internally by 
characterizing these relationships as "simple".

When you don't want that behavior, an alternate way to proceed is to 
specify the superclass relation explicitly by a call to setIs(), rather 
than via the contains= argument.  Then the replacement methods are not 
generated automatically.

In this example, the ii slot is treated differently.  (In fact, another 
view is that it should not have class "integer" at all, but something 
like "orderedInteger".)   But keeping close to the previous version, the 
difference is to replace the lines:
    setClass("OrderedAA",
       contains="AA",
by:
    setClass("OrderedAA",
             representation(ii = "integer"),
and then adding:
    setIs("OrderedAA", "AA",
          coerce = function(from) {
              new("AA", ii = [EMAIL PROTECTED]) #special case of:  copy all the 
inherited slots
          }
          )
Now there is no replace method to coerce in the other direction.  In 
fact, you get a warning to that effect when you run the setIs(), but in 
this example that would seem to be the behavior you want.

Now, the inherited coerce method can be selected:
 > as(aa, "OrderedAA")
I'm the A->OrderedAA coerce method
An object of class "OrderedAA"
Slot "ii":
[1] 2 3 4 6 8

and:
 > showMethods("coerce")
Function: coerce (package methods)
from="A", to="OrderedAA"
from="A", to="integer"
from="AA", to="OrderedAA"
    (inherited from: from="A", to="OrderedAA")
   etc.

Note that I'm not asserting this is obvious, at least not until the 
documentation gets some much-needed revision.  And it still requires a 
little extra work, but only in one spot and I think more logically 
motivated.

John


John Chambers wrote:
> Hi John,
>   
>> John Chambers wrote:
>>     
>>> Herve Pages wrote:
>>>       
>>>> Hi,
>>>>
>>>> It doesn't seem that the dispatching algo is finding my coerce 
>>>> method under
>>>> some circumstances.
>>>> Let's say I have 2 classes, A and AA and that AA is just a direct 
>>>> extension
>>>> of A with no additional slots:
>>>>
>>>>    setClass("A", representation(ii="integer"))
>>>>    setClass("AA", contains="A")
>>>>
>>>> I can define a method for coercing my objects to an integer vector 
>>>> with:
>>>>
>>>>    setAs("A", "integer", function(from) {cat("I'm the A->integer 
>>>> coerce method\n"); [EMAIL PROTECTED])
>>>>
>>>> and this works as expected when my object is an AA instance:
>>>>
>>>>    > aa <- new("AA", ii=sample(10, 5))
>>>>    > as(aa, "integer")
>>>>    I'm the A->integer coerce method
>>>>    [1] 10  1  6  4  7
>>>>
>>>> But things don't behave that way anymore if I introduce a direct 
>>>> extension of AA:
>>>>
>>>>    setClass("OrderedAA",
>>>>      contains="AA",
>>>>      validity=function(object)
>>>>      {
>>>>          if (!all(diff([EMAIL PROTECTED]) >= 0))
>>>>              return("slot 'ii' is not ordered")
>>>>          TRUE
>>>>      }
>>>>    )
>>>>
>>>> and a method for coercing an A object to an OrderedAA object:
>>>>
>>>>    setAs("A", "OrderedAA",
>>>>      function(from)
>>>>      {
>>>>          cat("I'm the A->OrderedAA coerce method\n")
>>>>          new("OrderedAA", ii=sort([EMAIL PROTECTED]))
>>>>      }
>>>>    )
>>>>
>>>> My A->OrderedAA coerce method is not called anymore:
>>>>
>>>>    > oaa <- as(aa, "OrderedAA")
>>>>    > oaa
>>>>    > validObject(oaa)
>>>>    Error in validObject(oaa) :
>>>>      invalid class "OrderedAA" object: slot 'ii' is not ordered
>>>>
>>>> This looks like a bug to me.
>>>>   
>>>>         
>>> Well, obscure perhaps, and not as well documented as it might be.
>>>
>>> Defining a subclass of "AA" creates implicit coerce methods in both 
>>> directions.  The method from "AA" to its subclass creates a new 
>>> object from the subclass, then inserts the inherited slots.
>>>
>>>  > selectMethod("coerce", c("AA", "OrderedAA"))
>>> Method Definition:
>>>
>>> function (from, to)
>>> {
>>>    obj <- new("OrderedAA")
>>>    as(obj, "AA") <- from
>>>    obj
>>> }
>>>       
>> The problem is that this implicit method doesn't seem to check the 
>> validity
>> of the new object. So now my users have an easy way to create broken 
>> objects
>> without being told that they are doing something wrong... unless I have
>> redefined a lot of coerce methods in my software (and there can be a 
>> lot of
>> them).
>> Unfortunately for me and my project, it looks like most of these 
>> "implicit"
>> methods are doing the wrong thing. So if the purpose of having them 
>> was to
>> make the developer's life easier, it doesn't work for me.
>>     
> Yes, I understand that.  But does that mean that all other applications 
> should NOT have the automatic replacement?  I'm not hugely committed to 
> it but notice that the real issue is the underlying
>   as(obj, "AA") <- from
> which uses a replacement method generated for simple contained 
> relationships.  Throwing that out to save you having to write a coerce 
> method seems a little drastic.
>
> The underlying assumption is that most classes are defined by the 
> classes of their slots. Your example has extra, implicit requirements, 
> which is fine but is not likely to come for free.
>   
>>> Signatures:
>>>        from to        target  "AA" "OrderedAA"
>>> defined "AA" "OrderedAA"
>>>
>>> The situation is made more confusing because these methods are only 
>>> explicitly inserted in the coerce() function the first time they're 
>>> used (for obvious efficiency reasons).
>>>       
>> Even worse, after I define my coerce method for A->OrderedAA, and 
>> _before_
>> I try to coerce my first AA object to an OrderedAA object, I get this:
>>
>>   > selectMethod("coerce", c("AA", "OrderedAA"))
>>   Method Definition:
>>
>>   function (from, to = "OrderedAA", strict = TRUE)
>>   {
>>     cat("I'm the A->OrderedAA coerce method\n")
>>     new("OrderedAA", ii = sort([EMAIL PROTECTED]))
>>   }
>>
>>   Signatures:
>>           from to
>>   target  "AA" "OrderedAA"
>>   defined "A"  "OrderedAA"
>>
>> which is not reporting the truth (the method that will actually be 
>> selected
>> will be the implicit one, not mine).
>>
>>     
>>> Notice that this is a direct method, not an inherited one.  It will 
>>> be chosen by the method selection from as().
>>>
>>> So it is true that if you want to override the implicit methods, you 
>>> have to do that for each new subclass, presumably when defining the 
>>> subclass.
>>>
>>>  >    setAs("AA", "OrderedAA",
>>> +      function(from)
>>> +      {
>>> +          cat("I'm the A->OrderedAA coerce method\n")
>>> +          new("OrderedAA", ii=sor .... [TRUNCATED]
>>>  > as(aa, "OrderedAA")
>>> I'm the A->OrderedAA coerce method
>>> An object of class "OrderedAA"
>>> Slot "ii":
>>> [1]  4  5  7  8 10
>>>
>>> It's possible to argue that an inherited, explicitly defined method, 
>>> as in your case, is worth more than a direct, implicitly defined method.
>>>       
>> It's definitely worth more than a direct, implicitly defined method that
>> produces invalid objects ;-)
>>
>> Anyway I can see why it can be nice (and save some efforts to the 
>> developer)
>> to have a coercion mechanism that works out-of-the-box, but I'm wondering
>> whether this is that useful to have implicit coerce methods in _both_
>> directions. It seems to me that, most of the times, the parent-to-child
>> direction will do the wrong thing, because, generally speaking, you need
>> some extra information (the extra slots) to coerce from the parent class
>> to the child class.
>>
>> Thanks for the clarification!
>>
>> H.
>>
>>     
>>> But whether this would be true in all applications is not obvious.
>>>
>>> But that the documentation needs to spell this out--no question.  
>>> Thanks for bringing it up.
>>>
>>> John
>>>
>>>
>>>       
>>>> Thanks,
>>>> H.
>>>>
>>>> ______________________________________________
>>>> R-devel@r-project.org mailing list
>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>>
>>>>   
>>>>         
>
> ______________________________________________
> R-devel@r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>
>   

        [[alternative HTML version deleted]]

______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

Reply via email to