From jmc at r-project.org  Thu Apr  1 20:44:46 2010
From: jmc at r-project.org (John Chambers)
Date: Thu, 01 Apr 2010 11:44:46 -0700
Subject: [Rd] as(1:4, "numeric") versus as.numeric(1:4, "numeric")
In-Reply-To: <4BB44BD2.3000207@fhcrc.org>
References: <4BB3D21B.3030306@fhcrc.org> <4BB3E053.9020709@r-project.org>
	<4BB44BD2.3000207@fhcrc.org>
Message-ID: <4BB4E99E.9080601@r-project.org>

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.

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.

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
>>>
>>>
>



[Rd] as(1:4, "numeric") versus as.numeric(1:4, "numeric")
John Chambers