From murdoch at stats.uwo.ca  Thu Sep  3 02:02:45 2009
From: murdoch at stats.uwo.ca (Duncan Murdoch)
Date: Wed, 02 Sep 2009 20:02:45 -0400
Subject: [Rd] Documentation for is.atomic and is.recursive
In-Reply-To: <4A9EF6AA.1010204@acm.org>
References: <8b356f880909021139pa07d922q1f1c28a0fb70cdbe@mail.gmail.com>		<4A9EC967.3080507@stats.uwo.ca>	<8b356f880909021310l3805de62g2c2467db9a1591d2@mail.gmail.com>
	<4A9EE3D8.7040008@stats.uwo.ca> <4A9EF6AA.1010204@acm.org>
Message-ID: <4A9F07A5.8090807@stats.uwo.ca>

On 02/09/2009 6:50 PM, Tony Plate wrote:
> Duncan Murdoch wrote:
>> On 02/09/2009 4:10 PM, Stavros Macrakis wrote:
>>> Let us stipulate that the current wording can be construed to be 
>>> correct.
>> I'd rather just state that the current wording is correct, without the 
>> weasel words.
>>
>>> I would nonetheless claim that the documentation as currently written
>>> is at best ambiguous and confusing, and would benefit from improved
>>> wording.
>> A claim that documentation would benefit from improved wording is a 
>> tautology.  A claim that the documentation is ambiguous requires more 
>> evidence than you've offered.  You have demonstrated that someone 
>> could be confused when reading it, but that isn't necessarily our 
>> responsibility.
>>
>> Duncan Murdoch
>>
> I suspect the confusion comes from the mistaken, but very 
> understandable, interpretation that the phrase "'x' is a vector" in the 
> documentation should somehow equate with the R function call 
> 'is.vector(x)'.  Similar potentially misleading wording appears in the 
> "Description" for ?is.vector:
>    "|is.vector| returns |TRUE| if |x| is a vector (of mode logical, 
> integer, real, complex, character, raw or list if not specified) or 
> expression and |FALSE|".
> However, the Details for ?is.vector do clarify, saying:
>   "|is.vector| returns |FALSE| if |x| has any attributes except names."
> 
> Here are some concrete suggestions for improving the documentation:
> (1) In the Details or Note section for ?is.atomic, add the paragraph:
> "is.atomic is unaffected by the presence of attributes on 'x', unlike 
> is.vector (which would probably be better named is.bare.vector).  
> is.atomic(x) merely tests whether the data mode of x is an atomic vector 
> type, and ignores whether or not x has attributes.  The behavior of 
> is.vector(x) is quite different -- it tests whether x is a bare vector.  
> is.vector can be TRUE for lists and expressions as well as atomic vector 
> types, but if there are attributes other than names on x, is.vector(x) 
> returns FALSE."
> 
> (2) In the Description section for ?is.vector, change
>   "|is.vector| returns |TRUE| if |x| is a vector (of mode logical, 
> integer, real, complex, character, raw or list if not specified) or 
> expression"
> to
>   "|is.vector| returns |TRUE| if |x| is a bare vector (of mode logical, 
> integer, real, complex, character, raw or list if not specified, with no 
> attributes other than names), or expression with no attributes other 
> than names"

Thanks for the suggestions.  I don't like your first one for a number of 
reasons:  too wordy, not quite right in its description of is.vector, 
editorializing about some other function's name, etc.  I'd rather leave 
the ?is.atomic page alone.

I agree that the ?is.vector page should be improved, but your suggestion 
is ambiguous:  does the "no attributes" part apply only to expressions? 
  I've just spent a few minutes, and come up with the following rewrite. 
  I'd shorten the Description entry to

   \code{is.vector} returns \code{TRUE} if \code{x} is a vector of the
specified mode having no attributes other than names.  It returns 
\code{FALSE} otherwise.

and lengthen the Details to

   If \code{mode = "any"}, \code{is.vector} returns \code{TRUE} for
   modes logical, integer, real, complex, character, raw, list or
   expression. It returns \code{FALSE} if \code{x} has any attributes
   except names.  (This is incompatible with S.)  On the other hand,
   \code{as.vector} removes \emph{all} attributes including names for
   results of atomic mode.

It's not perfect, because it is ambiguous what "This" refers to, but I 
don't actually know how much of the preceding description is 
incompatible with S, or even whether that claim makes sense:  Are all 
versions of S consistent in this?  I think it's probably inappropriate 
to be trying to document S here...  Nevertheless, I've committed this 
change.

It's not easy to write good documentation.

Duncan Murdoch

> 
> -- Tony Plate
> 
>>> What would be lost by that?
>>>
>>>> One could argue that in R's pre-history we should have had is.atomic 
>>>> imply
>>>> is.vector, but that's not how things are documented, and I think we're
>>>> pretty much stuck with the definitions we've got on low level 
>>>> functions like
>>>> those.
>>> I explicitly said in my mail that I was not suggesting that past
>>> design decisions (wise or unwise) be revisited; only that they be
>>> documented more clearly.
>>>
>>>                -s
>>>
>>> On Wed, Sep 2, 2009 at 3:37 PM, Duncan Murdoch<murdoch at stats.uwo.ca> 
>>> wrote:
>>>> On 9/2/2009 2:39 PM, Stavros Macrakis wrote:
>>>>> The documentation for is.atomic and is.recursive is inconsistent with
>>>>> their behavior in R 2.9.1 Windows.
>>>>>
>>>>> ? is.atomic
>>>>>
>>>>>     'is.atomic' returns 'TRUE' if 'x' is an atomic vector (or 'NULL')
>>>>>     and 'FALSE' otherwise.
>>>>>     ...
>>>>>     'is.atomic' is true for the atomic vector types ('"logical"',
>>>>>     '"integer"', '"numeric"', '"complex"', '"character"' and '"raw"')
>>>>>     and 'NULL'.
>>>>>
>>>>> This description implies that is.atomic(x) implies is.vector(x)
>>>>> (assuming that an "atomic vector type" is a subset of a "vector
>>>>> type").  But in fact that is not true for values with class
>>>>> attributes:
>>>> I don't see is.vector mentioned there.  The description of is.vector 
>>>> on its
>>>> own man page implies the behaviour below; I think the description of
>>>> is.atomic that you quote above is also consistent with the behaviour.
>>>>
>>>> One could argue that in R's pre-history we should have had is.atomic 
>>>> imply
>>>> is.vector, but that's not how things are documented, and I think we're
>>>> pretty much stuck with the definitions we've got on low level 
>>>> functions like
>>>> those.
>>>>
>>>>
>>>>> is.atomic(factor(3)) => TRUE
>>>>> is.vector(factor(3)) => FALSE
>>>>>
>>>>> is.atomic(table(3)) => TRUE
>>>>> is.vector(factor(3)) => FALSE
>>>>>
>>>>> It appears, then, that is.atomic requires only that unclass(x) be an
>>>>> atomic vector, not that x be an atomic vector.
>>>>>
>>>>> There is also another case where is.atomic(x) != 
>>>>> is.vector(unclass(x)):
>>>>>
>>>>> is.atomic(NULL) => TRUE
>>>>> is.vector(NULL) => FALSE
>>>>>
>>>>> It would be useful to make the documentation consistent with the
>>>>> implementation. (Presumably by updating the documentation, not
>>>>> changing the behavior.)
>>>>>
>>>>> The documentation continues:
>>>>>
>>>>>     'is.recursive' returns 'TRUE' if 'x' has a recursive (list-like)
>>>>>     structure and 'FALSE' otherwise.
>>>>>     ...
>>>>>     Most types of language objects are regarded as recursive: those
>>>>>     which are not are the atomic vector types, 'NULL' and symbols (as
>>>>>     given by 'as.name').
>>>>>
>>>>> But is.recursive(as.name('foo')) == is.recursive(quote(foo)) == FALSE.
>>>> That's what it says should happen.  symbols such as as.name('foo') 
>>>> are not
>>>> recursive.
>>>>
>>>> Duncan Murdoch
>>>>
>>>>> Again, it would be useful to make the documentation consistent with
>>>>> the implementation.
>>>>>
>>>>> To summarize all this in a table of the most common datatypes:
>>>>>
>>>>> outerl <-
>>>>>  function(f, a, b)
>>>>>    structure(outer(a,b,Vectorize(f)),
>>>>>              dimnames=list(a,b))
>>>>>
>>>>> outerl(function(x,f)(match.fun(f))(x),
>>>>>
>>>>> list(3,factor(c("a","b")),NULL,function()3,as.name("foo"),environment()), 
>>>>>
>>>>>
>>>>> list("class","mode","storage.mode","is.vector","is.atomic","is.recursive")) 
>>>>>
>>>>>
>>>>>              class         mode          storage.mode  is.vector
>>>>> is.atomic is.recursive
>>>>> 3             "numeric"     "numeric"     "double"      "TRUE"
>>>>> "TRUE"    "FALSE"           <<< OK
>>>>> 1:2           "factor"      "numeric"     "integer"     "FALSE"
>>>>> "TRUE"    "FALSE"             <<< inconsistent
>>>>> NULL          "NULL"        "NULL"        "NULL"        "FALSE"
>>>>> "TRUE"    "FALSE"         <<< inconsistent
>>>>> function ()   "function"    "function"    "function"    "FALSE"
>>>>> "FALSE"   "TRUE"          <<< OK
>>>>> foo           "name"        "name"        "symbol"      "FALSE"
>>>>> "FALSE"   "FALSE"          <<< inconsistent
>>>>> <environment> "environment" "environment" "environment" "FALSE"
>>>>> "FALSE"   "TRUE"     <<< OK
>>>>>
>>>>> Thanks,
>>>>>
>>>>>           -s
>>>>>
>>>>> ______________________________________________
>>>>> R-devel at r-project.org mailing list
>>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>> ______________________________________________
>> R-devel at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>



[Rd] Documentation for is.atomic and is.recursive
Duncan Murdoch