From lawrence.michael at gene.com  Fri Sep 22 19:53:34 2017
From: lawrence.michael at gene.com (Michael Lawrence)
Date: Fri, 22 Sep 2017 10:53:34 -0700
Subject: [Rd] S4 method implementation for S3 class
In-Reply-To: <CALEXWq0z7UQaoHzfvNbfvQ8Q3vkc5Nvu1Zw4VKSMM9vQzJpphw@mail.gmail.com>
References: <CALEXWq2Pfutuh1Na5cUaopUyup7WYPddd2HYKBhYGdxesabm0A@mail.gmail.com>
 <CAOQ5NycG-TAu-QAj74Z_LZOurNTPB-yRyGuCpOTkBpGfytvFoQ@mail.gmail.com>
 <CALEXWq0z7UQaoHzfvNbfvQ8Q3vkc5Nvu1Zw4VKSMM9vQzJpphw@mail.gmail.com>
Message-ID: <CAOQ5NyfS7dDLWm+W-k99YCndPd7C8C2WzU3Two3vSXFeeOBvfw@mail.gmail.com>

On Fri, Sep 22, 2017 at 10:28 AM, I?aki ?car <i.ucar86 at gmail.com> wrote:
> 2017-09-22 19:04 GMT+02:00 Michael Lawrence <lawrence.michael at gene.com>:
>> The %*% function is a primitive. As it says in the documentation under
>> ?Methods_Details
>>
>>      Methods may be defined for most primitives, and corresponding
>>      metadata objects will be created to store them. Calls to the
>>      primitive still go directly to the C code, which will sometimes
>>      check for applicable methods. The definition of ?sometimes? is
>>      that methods must have been detected for the function in some
>>      package loaded in the session and ?isS4(x)? is ?TRUE? for the
>>      first argument (or for the second argument, in the case of binary
>>      operators).
>>
>> But:
>>> isS4(x)
>> [1] FALSE
>>
>> I think this behavior is in the interest of performance. It avoids
>> adding S4 dispatch overhead to e.g. matrix objects.
>
> I see, thanks for the explanation.
>
>> In general, it's best to define an S4 class when using S4 dispatch,
>> but it sounds like you're stuck using some legacy S3 objects. In that
>> case, one would normally define an S3 method for `%*%()` that
>> delegates to a custom non-primitive generic, perhaps "matmult" in this
>> case. But since %*% is not an S3 generic, that's not an option.
>>
>> It would help to hear more about the context of the problem.
>
> This is a problem that Edzer Pebesma and I are facing in our packages
> units and errors respectively (you can find both on CRAN). They are
> designed in a similar way: units or errors are attached to numeric
> vectors as an attribute of an S3 class, and Ops, Math and Summary are
> redefined to cope with such units/errors.
>
> Then Edzer found that the %*% operator silently drops the attributes,
> so we were trying, without success, to set a method for our respective
> S3 classes to at least show a warning stating that the units/errors
> are being dropped.
>
> Ours are perfect use cases for S3 classes, and it would be a pitty if
> we have to switch everything to S4 just to show a warning. Clearly
> overkilling. Isn't there another way?
>

There is formally no such thing as an S3 class. Just S3 objects with a
class attribute. You could extend a base class with an S4 class, like
setClass("IntegerWithUnits", slots=c(units="Units"),
contains="integer"). Sure, that's a disruptive change, but it would be
in the right direction.

Extending base classes is always a risky proposition, as you've
discovered. Ideally you would override every transformation in order
to adjust or carry over the representation accordingly. The problem is
that there's a huge amount of transformations available for base
classes, mostly not encapsulated by generics.

Other possibilities include:
- Convincing someone to make %*% an internal S3 generic
- Promoting %*% to an R-level S3 generic, which would only work with
code that sees your namespace

Hopefully others have better ideas,
Michael

> I?aki
>



[Rd] S4 method implementation for S3 class
Michael Lawrence