From hpages at fhcrc.org  Thu Apr  1 23:59:09 2010
From: hpages at fhcrc.org (=?ISO-8859-1?Q?Herv=E9_Pag=E8s?=)
Date: Thu, 01 Apr 2010 14:59:09 -0700
Subject: [Rd] as(1:4, "numeric") versus as.numeric(1:4, "numeric")
In-Reply-To: <4BB4E99E.9080601@r-project.org>
References: <4BB3D21B.3030306@fhcrc.org> <4BB3E053.9020709@r-project.org>
	<4BB44BD2.3000207@fhcrc.org> <4BB4E99E.9080601@r-project.org>
Message-ID: <4BB5172D.7080905@fhcrc.org>

John Chambers wrote:
> The point I was making was that as() is not just a synonym for selecting 
> a method from coerce() by the usual inheritance rules.  I don't believe 
> it should be, and the documentation emphasizes that inheritance is not 
> used in the ordinary way.

I got this. If you look carefully at the change I'm suggesting for
selectMethod(), you will notice that I said that f="coerce" would
then need to become a special case.
In other words, when f="coerce", the usual inheritance rules are 
replaced by the rules that are currently implemented in as() and
described in its man page.
So to summarize: (1) the code in as() that is currently in charge of
selecting/creating/caching the most adequate coerce method is moved
to selectMethod(), (2) the sections in ?as that describe the rules
of this non-standard inheritance are moved to ?selectMethod.

> 
> If one were to start rewriting code (which I'm not suggesting) my 
> preference would be to  have coerce() not be a generic function, 
> eliminating the offending selectMethod() calls.

Then how one would know what as() is doing *exactly* i.e. which
coerce method was used or will be used in such or such situation?
showMethods()/selectMethod() are great tools because they allow the
developer to predict things and troubleshoot.

If you don't like putting the non-standard inheritance rules in
selectMethod() (when f="coerce") then you can always add something
like selectAsMethod() and put them here, and also add something
like showAsMethods(). I guess that's more or less what you are
saying when you propose to have coerce() not be a generic function,
at least not an usual one.
But it's important to expose selectAsMethod()/showAsMethods() to
the user. We absolutely need them!

Now I'm not sure I understand your concern about putting this
stuff in the existing selectMethod()/showMethods(). Why not just
ignore the useInheritance= arg when f="coerce"? Again, this would
be a special case anyway (and documented). The advantage of this
solution (over selectAsMethod()/showAsMethods()) is to avoid having
to introduce and expose 2 new names, so the user doesn't have to
switch between select*/show* tools depending on whether f="coerce"
or not.

H.

> 
> John
> 
> 
> On 4/1/10 12:31 AM, Herv? Pag?s wrote:
>> Hi John,
>>
>> John Chambers wrote:
>>> The example is confusing and debatable, but not an obvious bug.  And
>>> your presentation of it is the cause of much of the confusion
>>> (unintentionally I'm sure).
>>>
>>> First, slipping from the as() function to methods for the coerce()
>>> function might surprise a less experienced user.  And in fact, that
>>> is the point here.  If you look at the as() function, it jumps
>>> through several hoops and in particular selects a method from coerce
>>> in such a way as NOT to use inheritance on the from= argument.  (I
>>> think this makes sense in this case).  So I would assert that your
>>> selectMethod() output below came from a different session than the
>>> as(1:4, "numeric").
>>>
>>> Starting from a clean session with R 2.10.1:
>>>
>>> > class(as(1:4,"numeric"))
>>> [1] "integer"
>>> > selectMethod("coerce", c("integer","numeric"))
>>> Method Definition:
>>>
>>> function (from, to = "numeric", strict = TRUE)
>>> if (strict) {
>>>     class(from) <- "numeric"
>>>     from
>>> } else from
>>> <environment: namespace:methods>
>>>
>>> Signatures:
>>>         from      to
>>> target  "integer" "numeric"
>>> defined "integer" "numeric"
>>>
>>> Note, no call to as.numeric().  In a session without a previous call
>>> to as(), your selectMethod() call triggered a standard inherited
>>> method selection.  And if you had then gone on to as(), the result
>>> would have been different.
>>
>> Yes indeed. From a fresh start:
>>
>> > invisible(selectMethod("coerce", c("integer","numeric")))
>> > class(as(1:4, "numeric"))
>> [1] "numeric"
>>
>> But without the initial call to selectMethod(), as(1:4, "numeric")
>> returns an integer vector.
>>
>> Sorry but it's hard for me to understand the reasons for having
>> such behaviour, especially when selectMethod() is described as a
>> function "to *look* for a method corresponding to a given generic
>> function and signature". Apparently it does more than just looking...
>>
>>>
>>> In a different clean session:
>>>
>>>
>>> > getMethod("coerce", c("integer", "numeric"))
>>> Error in getMethod("coerce", c("integer", "numeric")) :
>>>   No method found for function "coerce" and signature integer, numeric
>>> > selectMethod("coerce", c("integer", "numeric"))
>>> Method Definition:
>>>
>>> function (from, to, strict = TRUE)
>>> {
>>>     value <- as.numeric(from)
>>>     if (strict)
>>>         attributes(value) <- NULL
>>>     value
>>> }
>>> <environment: namespace:methods>
>>>
>>> Signatures:
>>>         from      to
>>> target  "integer" "numeric"
>>> defined "ANY"     "numeric"
>>> > class(as(1:4,"numeric"))
>>> [1] "numeric"
>>>
>>> No argument about this being confusing.  Perhaps one should prohibit
>>> standard selectMethod() on coerce() but that seems a bit arcane to
>>> thwart folks like you!
>>>
>>> Suggested improvements for the current implementation are welcome, so
>>> long as they consider the best definition of as() in the general sense.
>>
>> So one problem seems to be that, on a fresh start, *both*
>>     as(1:4, "numeric")
>> and
>>     selectMethod("coerce", c("integer", "numeric"))
>> will cache a coerce method for the c("integer", "numeric") signature,
>> but they don't cache the *same* method!
>>
>> The automatic method cached by 'as(1:4, "numeric")' seems to be
>> coming from:
>>
>>   getClassDef("integer")@contains$numeric at coerce
>>
>> Maybe one way to improve things would be to modify this part of
>> the class definition for "integer" so it is in sync with
>>
>>   selectMethod("coerce", c("integer", "numeric")).
>>
>> There are other situations where the coerce methods are not
>> in sync:
>>
>> > getClassDef("factor")@contains$integer at coerce
>>   function (from, strict = TRUE)
>>   {
>>     attributes(from) <- NULL
>>     from
>>   }
>> <environment: namespace:methods>
>>
>> > selectMethod("coerce", c("factor", "integer"))
>>   Method Definition:
>>
>>   function (from, to, strict = TRUE)
>>   {
>>     value <- as.integer(from)
>>     if (strict)
>>         attributes(value) <- NULL
>>     value
>>   }
>> <environment: namespace:methods>
>>
>> That isn't a problem here because both methods will produce
>> the same result but is there any reason why the former
>> couldn't use the same code as the latter?
>>
>> A more radical approach would be to have a call to
>>
>>   selectMethod("coerce", c("integer", "numeric"))
>>
>> have the same effect on the table of coerce methods than a
>> call to
>>
>>   as(1:4, "numeric")
>>
>> i.e. the former will insert the same automatic method as the
>> latter. That means that all the hard work made by the as()
>> function in order to find/create/cache an appropriate method
>> would need to be moved to selectMethod() so in that function
>> 'f="coerce"' would become a special case.
>> Then as() would become a 10 line function (or less) that would
>> basically delegate to selectMethod("coerce", ...) to do the hard
>> work. This solution seems better to me as it would then guarantee
>> consistency between what as() does and what
>> selectMethod("coerce", ...) says.
>>
>> Cheers,
>> H.
>>
>>>
>>> Regards,
>>>   John
>>>
>>> On 3/31/10 3:52 PM, Herv? Pag?s wrote:
>>>> Hi,
>>>>
>>>> > class(as(1:4, "numeric"))
>>>>   [1] "integer"
>>>>
>>>> Surprising but an explanation could be that an integer
>>>> vector being a particular case of numeric vector, this
>>>> coercion has nothing to do because 1:4 is already numeric.
>>>> And indeed:
>>>>
>>>> > is.numeric(1:4)
>>>>   [1] TRUE
>>>> > is.numeric(as(1:4, "numeric"))
>>>>   [1] TRUE
>>>>
>>>> However, 'as(1:4, "numeric")' is inconsistent with
>>>>
>>>> > class(as.numeric(1:4))
>>>>   [1] "numeric"
>>>>
>>>> And, even more confusing, if you look at the coerce,ANY,numeric
>>>> method:
>>>>
>>>> > selectMethod("coerce", c("integer", "numeric"))
>>>>   Method Definition:
>>>>
>>>>   function (from, to, strict = TRUE)
>>>>   {
>>>>     value <- as.numeric(from)
>>>>     if (strict)
>>>>         attributes(value) <- NULL
>>>>     value
>>>>   }
>>>> <environment: namespace:methods>
>>>>
>>>>   Signatures:
>>>>           from      to
>>>>   target  "integer" "numeric"
>>>>   defined "ANY"     "numeric"
>>>>
>>>> it calls as.numeric()!
>>>>
>>>> So how can 'as(1:4, "numeric")' not return the same thing as
>>>> 'as.numeric(1:4)' looks like a mystery to me. Could it be
>>>> conceivable that I found a bug?
>>>>
>>>> Cheers,
>>>> H.
>>>>
>>>>
>>>> > sessionInfo()
>>>> R version 2.11.0 Under development (unstable) (2010-03-15 r51282)
>>>> x86_64-unknown-linux-gnu
>>>>
>>>> locale:
>>>>  [1] LC_CTYPE=en_CA.UTF-8       LC_NUMERIC=C
>>>>  [3] LC_TIME=en_CA.UTF-8        LC_COLLATE=en_CA.UTF-8
>>>>  [5] LC_MONETARY=C              LC_MESSAGES=en_CA.UTF-8
>>>>  [7] LC_PAPER=en_CA.UTF-8       LC_NAME=C
>>>>  [9] LC_ADDRESS=C               LC_TELEPHONE=C
>>>> [11] LC_MEASUREMENT=en_CA.UTF-8 LC_IDENTIFICATION=C
>>>>
>>>> attached base packages:
>>>> [1] stats     graphics  grDevices utils     datasets  methods   base
>>>>
>>>>
>>

-- 
Herv? Pag?s

Program in Computational Biology
Division of Public Health Sciences
Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N, M2-B876
P.O. Box 19024
Seattle, WA 98109-1024

E-mail: hpages at fhcrc.org
Phone:  (206) 667-5791
Fax:    (206) 667-1319



[Rd] as(1:4, "numeric") versus as.numeric(1:4, "numeric")
=?ISO-8859-1?Q?Herv=E9_Pag=E8s?=