[R] Fine controlling "three dots" argument dispatch to functions with identical argument names

Jeff Newmiller jdnewmil at dcn.davis.CA.us
Sun Nov 16 19:54:54 CET 2014


You have some interesting ideas about what makes for improvements in parameter interfaces. Wrapping the arguments into a list is like creating an object to represent all of them, except that you don't have the benefits of a class to go with that cognitive shift. And if making classes to hold parameters were appropriate wouldn't you have already done so in the foo and bar interfaces? That is a heavyweight approach that doesn't always make sense.

I agree with Duncan that each time you define a function you are defining an interface that should stand on its own... the user should be able to associate unique names with unique behaviors. From this perspective, your reluctance to define a unified set of uniquely-named parameters for each of foobar and (and apparently foo) seems illogical.
---------------------------------------------------------------------------
Jeff Newmiller                        The     .....       .....  Go Live...
DCN:<jdnewmil at dcn.davis.ca.us>        Basics: ##.#.       ##.#.  Live Go...
                                      Live:   OO#.. Dead: OO#..  Playing
Research Engineer (Solar/Batteries            O.O#.       #.O#.  with
/Software/Embedded Controllers)               .OO#.       .OO#.  rocks...1k
--------------------------------------------------------------------------- 
Sent from my phone. Please excuse my brevity.

On November 16, 2014 8:42:20 AM PST, Janko Thyson <janko.thyson at gmail.com> wrote:
>Thanks for the info/suggestions!
>
>But note that it's not just a one-step, but a two step dispatching
>process
>with respect to `...`. That is, `foo()` and `bar()` are *not* both
>called
>directly inside `foobar()`: `foobar()` only calls `foo()` which then
>calls
>`bar()`.
>
>I now came up with something along the lines of what Duncan suggested.
>The
>reason I wouldn't want to go with Jeff's approach is that I would want
>`foobar()` to remain as generic an interface as possible (the same goes
>for
>`foo()` calling `bar()`).
>
>I.e., I don't want it to have any explicit arguments of subsequently
>called
>functions (e.g. `y_foo`). It should just be able to take any inputs
>that
>subsequently called functions can process (i.e. `foo()` and then in
>turn
>`bar()`) and pass them along accordingly. Of course this would need to
>be
>clearly and well documented for the respective functions.
>
>So here's my current approach. It would be nice to just be able to
>dispatch
>`...` for calls of `do.call()` like so: `do.call("foo", c(x = x, ...))`
>but
>that way a nested structure of `...` gets flattened out (see respective
>lines in `foobar()`). That's why I need to resort to `do.call("foo",
>c(x =
>x, threedots$args_foo, threedots[-idx]))`. What do you think of it?
>
>foobar <- function(x, ...) {
>  message("foobar ----------")
>  message("foobar/threedots")
>  threedots <- list(...)
>  try(print(threedots))
>  message("foobar/combined args")
>  try(print(c(x, threedots)))
>## --> list gets flattened (i.e. `args_foo.y` instead of nested
>structure)
>  ## --> that's why subsequent functions will not recognize "their"
>arguments
>  ## from it
>  if (any(idx <- names(threedots) %in% "args_foo")) {
>    do.call("foo", c(x = x, threedots$args_foo, threedots[-idx]))
>  } else {
>    foo(x = x, ...)
>  }
>}
>foo <- function(x, y = "some character", ...) {
>  message("foo ----------")
>  message("foo/threedots")
>  threedots <- list(...)
>  try(print(threedots))
>  message("foo/y")
>  try(print(y))
>  if (any(idx <- names(threedots) %in% "args_bar")) {
>    do.call("bar", c(x = x, threedots$args_bar, threedots[-idx]))
>  } else {
>    bar(x = x, ...)
>  }
>}
>bar <- function(x, y = TRUE, ...) {
>  message("bar ----------")
>  message("bar/threedots")
>  try(print(list(...)))
>  message("bar/y")
>  try(print(y))
>  return(paste0("hello: ", x))
>}
>
>foobar(x = "John Doe", args_foo = list(y = "hello world!"))
>foobar(x = "John Doe", args_bar = list(y = FALSE))
>foobar(x = "John Doe",
>       args_foo = list(y = "hello world!"),
>       args_bar = list(y = FALSE)
>)
>
>Best regards and thanks,
>Janko
>
>On Sat, Nov 15, 2014 at 6:10 PM, Duncan Murdoch
><murdoch.duncan at gmail.com>
>wrote:
>
>> On 15/11/2014, 11:26 AM, Jeff Newmiller wrote:
>> > AFAIK You have to alter the name of at least one of the y arguments
>as
>> used by foobar, and anyone calling foobar has to read about that in
>the
>> help file. That is only one y can be in "...". e.g.
>> >
>> > foobar <- function( x, y_foo, ... ) {
>> >   foo( x, y=y_foo, ... )
>> >   bar( x, ... )
>> > }
>> >
>>
>> That's the best solution.  There is another one:  you can put
>>
>> args <- list(...)
>>
>> into foobar(), and then do whatever you like to the args vector, and
>put
>> together calls to foo() and bar() using do.call().  But this is hard
>to
>> read and easy to get wrong, so I recommend Jeff's simple solution.
>>
>> Duncan Murdoch
>>
>> >
>> >
>>
>---------------------------------------------------------------------------
>> > Jeff Newmiller                        The     .....       .....  Go
>> Live...
>> > DCN:<jdnewmil at dcn.davis.ca.us>        Basics: ##.#.       ##.#. 
>Live
>> Go...
>> >                                       Live:   OO#.. Dead: OO#.. 
>Playing
>> > Research Engineer (Solar/Batteries            O.O#.       #.O#. 
>with
>> > /Software/Embedded Controllers)               .OO#.       .OO#.
>> rocks...1k
>> >
>>
>---------------------------------------------------------------------------
>> > Sent from my phone. Please excuse my brevity.
>> >
>> > On November 15, 2014 6:49:41 AM PST, Janko Thyson <
>> janko.thyson at gmail.com> wrote:
>> >> Dear list,
>> >>
>> >> I wonder if there's a clever way to fine control the exact way
>> >> arguments
>> >> are dispatched via R's "three dots" argument ....
>> >>
>> >> Consider the following use case:
>> >>
>> >> - you have a function foobar() that calls foo() which in turn
>calls
>> >> bar()
>> >> - *both* foo() and bar() have an argument that's called y, but
>they
>> >> each
>> >>   have a *different meaning*
>> >> - in the call to foobar(), you would like to say "here's the y for
>> >> foo()
>> >> and here's the y for bar()". *That's what I would like to
>accomplish*.
>> >>
>> >> If you simply call foobar(x = "John Doe", y = "hello world"), y
>only
>> >> get's
>> >> dispatched to foo() as in the call to bar() things would have to
>be
>> >> explicit in order to be dispatched (i.e. the call would have to be
>> >> bar(x =
>> >> x, y = y) instead of bar(x = x, ...):
>> >>
>> >> foo <- function(x, y = "some character", ...) {
>> >>  message("foo ----------")
>> >>  message("foo/threedots")
>> >>  try(print(list(...)))
>> >>  message("foo/y")
>> >>  try(print(y))
>> >>  bar(x = x, ...)}
>> >> bar <- function(x, y = TRUE, ...) {
>> >>  message("bar ----------")
>> >>  message("bar/threedots")
>> >>  try(print(list(...)))
>> >>  message("bar/y")
>> >>  try(print(y))
>> >>  return(paste0("hello: ", x))}
>> >> foobar <- function(x, ...) {
>> >>  message("foobar ----------")
>> >>  message("foobar/threedots")
>> >>  try(print(list(...)))
>> >>  foo(x = x, ...)}
>> >>
>> >> foobar(x = "John Doe", y = "hi there")# foobar ----------#
>> >> foobar/threedots# $y# [1] "hi there"# # foo ----------#
>foo/threedots#
>> >> list()# foo/y# [1] "hi there"# bar ----------# bar/threedots#
>list()#
>> >> bar/y# [1] TRUE# [1] "hello: John Doe"
>> >>
>> >> What I conceptionally would like to be able to do is something
>like
>> >> this:
>> >>
>> >> foobar(x = "John Doe", y_foo = "hello world!", y_bar = FALSE)
>> >>
>> >> Here's an approach that works but that also feels very odd:
>> >>
>> >> foo <- function(x, y = "some character", ...) {
>> >>  message("foo ----------")
>> >>  message("foo/threedots")
>> >>  try(print(list(...)))
>> >>  message("foo/y")
>> >>  arg <- paste0("y_", sys.call()[[1]])
>> >>  if (arg %in% names(list(...))) {
>> >>    y <- list(...)[[arg]]
>> >>  }
>> >>  try(print(y))
>> >>  bar(x = x, ...)}
>> >> bar <- function(x, y = TRUE, ...) {
>> >>  message("bar ----------")
>> >>  message("bar/threedots")
>> >>  try(print(list(...)))
>> >>  message("bar/y")
>> >>  arg <- paste0("y_", sys.call()[[1]])
>> >>  if (arg %in% names(list(...))) {
>> >>    y <- list(...)[[arg]]
>> >>  }
>> >>  try(print(y))
>> >>  return(paste0("hello: ", x))}
>> >>
>> >> foobar(x = "John Doe", y_foo = "hello world!", y_bar = FALSE)#
>foobar
>> >> ----------# foobar/threedots# $y_foo# [1] "hello world!"# #
>$y_bar#
>> >> [1] FALSE# # foo ----------# foo/threedots# $y_foo# [1] "hello
>> >> world!"# # $y_bar# [1] FALSE# # foo/y# [1] "hello world!"# bar
>> >> ----------# bar/threedots# $y_foo# [1] "hello world!"# # $y_bar#
>[1]
>> >> FALSE# # bar/y# [1] FALSE# [1] "hello: John Doe"
>> >>
>> >> How would you go about implementing something like this?
>> >>
>> >> I also played around with S4 method dispatch to see if I could
>define
>> >> methods for a signature argument ..., but that didn't go too well
>(and
>> >> it's
>> >> probably a very bad idea anyway):
>> >>
>> >> setGeneric(
>> >>  name = "foo",
>> >>  signature = c("x", "..."),
>> >>  def = function(x, ...) standardGeneric("foo")      )
>> >> setMethod(
>> >>  f = "foo",
>> >>  signature = signature(x = "character", "..." =
>"MyThreeDotsForBar"),
>> >> definition = function(x, ...) bar(x = x))## --> does not work
>> >>
>> >>      [[alternative HTML version deleted]]
>> >>
>> >> ______________________________________________
>> >> R-help at r-project.org mailing list
>> >> https://stat.ethz.ch/mailman/listinfo/r-help
>> >> PLEASE do read the posting guide
>> >> http://www.R-project.org/posting-guide.html
>> >> and provide commented, minimal, self-contained, reproducible code.
>> >
>> > ______________________________________________
>> > R-help at r-project.org mailing list
>> > https://stat.ethz.ch/mailman/listinfo/r-help
>> > PLEASE do read the posting guide
>> http://www.R-project.org/posting-guide.html
>> > and provide commented, minimal, self-contained, reproducible code.
>> >
>>
>>



More information about the R-help mailing list