Skip to content

Qs: The list of arguments, wrapping functions...

8 messages · Kynn Jones, Steve Weston, Hadley Wickham +2 more

#
On Tue, May 19, 2009 at 4:22 PM, Kynn Jones <kynnjo at gmail.com> wrote:

            
How about something like this:

    funnames <- c('f1', 'f2', 'f3')

    funfactory <- function(n) {
      f <- function(...) NULL
      body(f) <- substitute(REPLACE(...), list(REPLACE=as.name(n)))
      f
    }

    funlist <- lapply(funnames, funfactory)

"funlist" is a list of function objects that wrap calls to the
named functions.
You should read chapter 6 of the "R Language Definition" manual, which is
titled "Computing on the language".
#
match.call ?

Hadley
#
Kynn Jones wrote:
a quick shot from a naive r user:

    f = function(a=1, b, ...)
        as.list(match.call()[-1])

    f(2)
    f(b=2)
    f(1,2,3)
what do you mean, precisely?
another quick shot from a naive r user:

    f = function()
       assign(
           as.character(match.call()[[1]]),
           function() evil(),
           envir=parent.frame())
      
    f
    f()
    f

you can then use stuff like formals, body, match.call, parent.frame,
etc. to have your function reimplement itself based on how and where it
is called.
recall that decorators, when applied using the @syntax, do not just
return a new function, but rather redefine the one to which they are
applied.  so in r it would not be enough to write a function that takes
a function and returns another one;  it'd have to establish the input
function's name and the environment it resides in, and then replace that
entry in that environment with the new function.

yet another quick shot from the same naive r user:

    # the decorator operator
    '%@%' = function(decorator, definition) {
       definition = substitute(definition)
       name = definition[[2]][[2]]
       definition = definition[[2]][[3]]
       assign(
           as.character(name),
           decorator(eval(definition, envir=parent.frame())),
           envir=parent.frame()) }

    # a decorator
    twice = function(f)
       function(...)
           do.call(f, as.list(f(...)))

    # a function
    inv = function(a, b)
       c(b, a)

    inv(1,2)
    # 2 1
    twice(inv)(1,2)
    # 1 2

    # a decorated function
    twice %@% {
       square = function(x) x^2 }

    square(2)
    # 16

    # another decorator
    verbose = function(f)
       function(...) {
          cat('computing...\n')
          f(...) }

    # another decorated function
    verbose %@% {
       square = function(x) x^2 }

    square(2)
    # computing...
    # 4

there is certainly a lot of space for improvements, and there are
possibly bugs in the code above, but i hope it helps a little.

vQ
#
Wacek Kusnierczyk wrote:
or maybe

    f = function()
       body(f) <<- expression(evil())
vQ
#
Wacek Kusnierczyk wrote:
though, 'of course', these two versions are not effectively equivalent; try

    g = f
    f()
    c(g, f)

with both definitions.

vQ
#
match.call() will return the call.   merge.zoo in the zoo package
uses it if you need an example.  as.list(match.call()) will return
a list.

list(...) will return the ... arguments as a list.

$.proto in the proto package allows one to write p$f where p
is a proto object and f is a function and p$f is the function
f(p, ...), i.e. it provides a currying operation.

The Defaults package allows one to dynamically change the
default arguments of functions.
On Tue, May 19, 2009 at 4:22 PM, Kynn Jones <kynnjo at gmail.com> wrote: