Re: [Rd] S4 accessors

From: John Chambers <jmc_at_r-project.org>
Date: Thu 28 Sep 2006 - 14:08:38 GMT

Maybe this was not as obvious or well-known as I assumed would be the case on this list.

In an OOP system (I dislike the term but let's use it), the programming mechanism is to invoke a method on an object. Say method flag on object x:   x$flag(....)
(using "$" in R style instead of the usual "." in Java, et al.)

The key conceptual difference from
  flag(x, ....)
in a functional language is that the flag method invocation in the OOP language is always part of the class definition for class(x). There is no inherent reason why two methods named flag() would have any connection if the two classes involved had no connection, even if they were in the same package.

That's fundamentally different for the user in R, where functions (plot() to take a strong example) come with their own mental model. (If you had a slot named "plot", you would be unwise to have an accessor of the same name, but get_plot() would be unambiguous.) The point is that in a functional language function names are like verbs and tend to mean something, as they should. It is not an assertion that all functions are meaningful for all objects.

Even OOP languages often use essentially the same sort of syntactic convention discussed in this thread (see Java Beans, for example). Henrik's mail gave some R examples.

Having an accessor mechanism would allow accessors to be generated automatically, including specifying cases where you don't want an explicit set_foo() method. Again, see systems like Java Beans for a somewhat similar approach.

If you want to name accessor methods to disguise the fact that they relate to a specific slot, you can always do that. But a syntactic convention could implement a current design automatically, without preventing future software from changing the set of slots while retaining the earlier accessor method names, though now they won't have the same implementation.

We don't have such a mechanism now, so you can do whatever seems best. A decision to have accessor methods for _all_ slots would likely lead to conflicts with function names eventually, unless some syntactic convention was used or the slot names themselves were forced to follow some naming convention.

Ross Boylan wrote:
> I'm trying to understand what the underlying issues are here--with the
> immediate goal of how that affects my design and documentation
> decisions.
>
> On Wed, Sep 27, 2006 at 02:08:34PM -0400, John Chambers wrote:
>
>> Seth Falcon wrote:
>>
>>> John Chambers <jmc@r-project.org> writes:
>>>
>>>
>>>
>>>> There is a point that needs to be remembered in discussions of
>>>> accessor functions (and more generally).
>>>>
>>>> We're working with a class/method mechanism in a _functional_
>>>> language. Simple analogies made from class-based languages such as
>>>> Java are not always good guides.
>>>>
>>>> In the example below, "a function foo that only operates on that
>>>> class" is not usually a meaningful concept in R.
>>>>
>
> The sense of "meaningful" here is hard for me to pin down, even with
> the subsequent discussion.
>
> I think the import is more than formal: R is not strongly typed, so
> you can hand any argument to any function and the language will not
> complain.
>
>
>>>>
>>>>
>>> If foo is a generic and the only method defined is for class Bar, then
>>> the statement seems meaningful enough?
>>>
>>>
>> This is not primarily a question about implementation but about what the
>> user understands. IMO, a function should have an intuitive meaning to
>> the user. Its name is taking up a "global" place in the user's brain,
>> and good software design says not to overload users with too many
>> arbitrary names to remember.
>>
>
> It's true that clashing uses of the same name may lead to confusion,
> but that need not imply that functions must be applicable to all
> objects. Many functions only make sense in particular contexts, and
> sometimes those contexts are quite narrow.
>
> One of the usual motivations for an OO approach is precisely to limit
> the amount of global space taken up by, for example, functions that
> operate on the class (global in both the syntactic sense and in the
> inside your brain sense). Understanding a traditional OO system, at
> least for me, is fundamentally oriented to understanding the objects
> first, with the operations on them as auxiliaries. As you point out,
> this is just different from the orientation of a functional language,
> which starts with the functions.
>
>
>> To be a bit facetious, if "flag is a slot in class Bar, it's really not
>> a good idea to define the accessor for that slot as
>> flag <- function(object)object@flag
>>
>> Nor is the situation much improved by having flag() be a generic, with
>> the only method being for class Bar. We're absconding with a word that
>> users might think has a general meaning. OK, if need be we will have
>> different flag() functions in different packages that have _different_
>> intuitive interpretations, but it seems to me that we should try to
>> avoid that problem when we can.
>>
>> OTOH, it's not such an imposition to have accessor functions with a
>> syntax that includes the name of the slot in a standardized way:
>> get_flag(object)
>> (I don't have any special attachment to this convention, it's just there
>> for an example)
>>
>
> I don't see why get_flag differs from flag; if "flag" lends itself to
> multiple interpretations or meanings, wouldn't "get_flag" have the
> same problem?
>
> Or are you referring to the fact that "flag" sounds as if it's a verb
> or action? That's a significant ambiguity, but there's nothing about
> it that is specific to a functional approach.
>
>
>>
>>
>>>
>>>
>>>> Functions are first-class objects and in principle every function
>>>> should have a "function", a purpose. Methods implement that purpose
>>>> for particular combinations of arguments.
>>>>
>>>>
>
> If this is a claim that every function should make sense for every
> object, it's asking too much. If it's not, I don't really see how a
> function can avoid having a purpose. The purpose of accessor
> functions is to get or set the state of the object.
>
>
>>>> Accessor functions are therefore a bit anomalous.
>>>>
>>>>
>>> How? A given accessor function has the purpose of returning the
>>> expected data "contained" in an instance. It provides an abstract
>>> interface that decouples the structure of the class from the data it
>>> needs to provide to users.
>>>
>>>
>> See above. That's true _if_ the name or some other syntactic sugar
>> makes it clear the this is indeed an accessor function, but not otherwise.
>>
>
> Aside from the fact that I don't see why get_flag is so different from
> flag, the syntactic sugar argument has another problem. The usually
> conceived purpose of accessors is to hide from the client the
> internals of the object. To take an example that's pretty close to
> one of my classes, I want startTime, endTime, and duration.
> Internally, the object only needs to hold 2 of these quantities to get
> the 3rd, but I don't want the client code to be aware of which choice
> I made. In particular, I don't what the client code to change from
> duration to get_duration if I switch to a representation that stored
> the duration as a slot.
>
> Ross
>
>

        [[alternative HTML version deleted]]



R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel Received on Fri Sep 29 00:33:19 2006

Archive maintained by Robert King, hosted by the discipline of statistics at the University of Newcastle, Australia.
Archive generated by hypermail 2.1.8, at Thu 28 Sep 2006 - 15:30:10 GMT.

Mailing list information is available at https://stat.ethz.ch/mailman/listinfo/r-devel. Please read the posting guide before posting to the list.