From maechler at stat.math.ethz.ch  Fri May  4 15:02:22 2018
From: maechler at stat.math.ethz.ch (Martin Maechler)
Date: Fri, 4 May 2018 15:02:22 +0200
Subject: [Rd] length of `...`
In-Reply-To: <3ffcd05b-6417-87be-94e0-f621c4a3116b@fredhutch.org>
References: <d0b68216-4922-2249-e5e6-1589345dc6d7@kogentum.hu>
 <CAF8bMcaRW88VdyXw-kmCa9dw-awBiNiU+j+nktLRACCj4grPdQ@mail.gmail.com>
 <37141c6a-831b-b74a-7abf-a36d64130f81@gmail.com>
 <CABdHhvHzGECDQBsZsXTbakNGW7JuX4tw46_KmcYX8_b3kMB=Fg@mail.gmail.com>
 <3ffcd05b-6417-87be-94e0-f621c4a3116b@fredhutch.org>
Message-ID: <23276.23006.334586.473944@stat.math.ethz.ch>

>>>>> Herv? Pag?s <hpages at fredhutch.org>
>>>>>     on Thu, 3 May 2018 08:55:20 -0700 writes:

    > Hi,
    > It would be great if one of the experts could comment on the
    > difference between Hadley's dotlength and ...length? The fact
    > that someone bothered to implement a new primitive for that
    > when there seems to be a very simple and straightforward R-only
    > solution suggests that there might be some gotchas/pitfalls with
    > the R-only solution.

Namely

> dotlength <- function(...) nargs()

> (This is subtly different from calling nargs() directly as it will
> only count the elements in ...)

> Hadley


Well,  I was the "someone".  In the past I had seen (and used myself)

   length(list(...))       

and of course that was not usable.
I knew of some substitute() / match.call() tricks [but I think
did not know Bill's cute substitute(...()) !] at the time, but
found them too esoteric.

Aditionally and importantly,  ...length()  and  ..elt(n)  were
developed  "synchronously",  and the R-substitutes for ..elt()
definitely are less trivial (I did not find one at the time), as
Duncan's example to Bill's proposal has shown, so I had looked
at .Primitive() solutions of both.

In hindsight I should have asked here for advice,  but may at
the time I had been a bit frustrated by the results of some of
my RFCs ((nothing specific in mind !))

But __if__ there's really no example where current (3.5.0 and newer)

  ...length()

differs from Hadley's  dotlength()
I'd vert happy to replace ...length 's C based definition by
Hadley's beautiful minimal solution.

Martin


    > On 05/03/2018 08:34 AM, Hadley Wickham wrote:
    >> On Thu, May 3, 2018 at 8:18 AM, Duncan Murdoch <murdoch.duncan at gmail.com> wrote:
    >>> On 03/05/2018 11:01 AM, William Dunlap via R-devel wrote:
    >>>> 
    >>>> In R-3.5.0 you can use ...length():
    >>>> > f <- function(..., n) ...length()
    >>>> > f(stop("one"), stop("two"), stop("three"), n=7)
    >>>> [1] 3
    >>>> 
    >>>> Prior to that substitute() is the way to go
    >>>> > g <- function(..., n) length(substitute(...()))
    >>>> > g(stop("one"), stop("two"), stop("three"), n=7)
    >>>> [1] 3
    >>>> 
    >>>> R-3.5.0 also has the ...elt(n) function, which returns
    >>>> the evaluated n'th entry in ... , without evaluating the
    >>>> other ... entries.
    >>>> > fn <- function(..., n) ...elt(n)
    >>>> > fn(stop("one"), 3*5, stop("three"), n=2)
    >>>> [1] 15
    >>>> 
    >>>> Prior to 3.5.0, eval the appropriate component of the output
    >>>> of substitute() in the appropriate environment:
    >>>> > gn <- function(..., n) {
    >>>> +   nthExpr <- substitute(...())[[n]]
    >>>> +   eval(nthExpr, envir=parent.frame())
    >>>> + }
    >>>> > gn(stop("one"), environment(), stop("two"), n=2)
    >>>> <environment: R_GlobalEnv>
    >>>> 
    >>> 
    >>> Bill, the last of these doesn't quite work, because ... can be passed down
    >>> through a string of callers.  You don't necessarily want to evaluate it in
    >>> the parent.frame().  For example:
    >>> 
    >>> x <- "global"
    >>> f <- function(...) {
    >>> x <- "f"
    >>> g(...)
    >>> }
    >>> g <- function(...) {
    >>> firstExpr <- substitute(...())[[1]]
    >>> c(list(...)[[1]], eval(firstExpr, envir = parent.frame()))
    >>> }
    >>> 
    >>> Calling g(x) correctly prints "global" twice, but calling f(x) incorrectly
    >>> prints
    >>> 
    >>> [1] "global" "f"
    >>> 
    >>> You can get the first element of ... without evaluating the rest using ..1,
    >>> but I don't know a way to do this for general n in pre-3.5.0 base R.
    >> 
    >> If you don't mind using a package:
    >> 
    >> # works with R 3.1 and up
    >> library(rlang)
    >> 
    >> x <- "global"
    >> f <- function(...) {
    >> x <- "f"
    >> g(...)
    >> }
    >> g <- function(...) {
    >> dots <- enquos(...)
    >> eval_tidy(dots[[1]])
    >> }
    >> 
    >> f(x, stop("!"))
    >> #> [1] "global"
    >> g(x, stop("!"))
    >> #> [1] "global"
    >> 
    >> Hadley
    >> 

    > -- 
    > Herv? Pag?s

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

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

    > ______________________________________________
    > R-devel at r-project.org mailing list
    > https://stat.ethz.ch/mailman/listinfo/r-devel



[Rd] length of `...`
Martin Maechler