Message-Id: <9106181842.AA01981@umnstat.stat.umn.edu> From: "Luke Tierney" To: S-news@stat.wisc.edu Subject: Re: functions that create functions Date: Tue, 18 Jun 91 13:42:36 CDT Mark Longtin writes: Does anyone know why this doesn't work or how to make it work? >power <- function(p) function(x) x^p >square <- power(2) >square(3) Error in call to "square": Object "p" not found Trevor Hastie and Allan Wilks propose a solution that uses `substitute': > power <- function(p) substitute(function(x) x^p) > square <- power(2) > square function(x) x^2 > square(3) [1] 9 Allan Zaslavski proposes a more complex solution. For many purposes the `subsitute' solution works fine. But there are a few pitfalls. For example: > power <- function(p) substitute(function(x) x^p) > x<-2 > square<-power(x) > square(3) [1] 27 #????? The explanation is: > square function(x) x^x By the evaluation rules, the expression is substituted for p, not the value of the expression. Nothing protects you from the fact that your function argument `x' also appears in the expression. As another example, > pw<-function(p) { x<-3; substitute(function(x) x^p); } > pw(2) function(x) 3^2 substitute does not recognize that the `x' in the function body is the argument to the function; it replaces it by the value of the local variable `x' in the calling frame. A solution along the lines proposed by Allan Zaslavski avoids these problems. The reason Mark's original verison does not work is that it assumes `lexical scoping' of free variables. S uses a scoping rule in which all free variables are global variables. The approach based on lexical scoping is standard in Common Lisp or Scheme and works because function closures contain information about their defining environment. S's scoping rule, together with the lazy evaluation mechanism, make developing a general mechanism of forming closures more difficult. If S is modified to allow function objects to include a data frame, then this will become easier. (I seem to recall that this was discussed at some point for the New Release, but I don't remember if it has actually become pert of that or not.) In the meantime, one general approach is to stuff the free variable bindings you want into the funciton argument list as arguments with specified default bindings. Here is an approach I have used. Define the function `make.closure' as > make.closure <- function(f, args) { args <- as.list(args) if(len(args) > 0 && any(names(args) == "")) stop(paste("all arguments are not named:", args)) if(len(f) == 1) f <- c(args, f) else f <- c(f[1:(len(f) - 1)], args, f[len(f)]) mode(f) <- "function" return(f) } Then call `make.closure' with your function and a list of the free variables you want your function to use: > make.closure(function(x)x^p,list(p=x)) function(x, p = 2) x^p > f<-function(x)x^p > make.closure(f,list(p=2)) function(x, p = 2) x^p > make.closure(function(x) (x+a)^p,list(a=3,p=5)) function(x, a = 3, p = 5) (x + a)^p