Learn R Programming

mvbutils (version 1.1.1)

mlocal: Macro-like functions

Description

mlocal lets you write a function whose statements are executed in its caller's frame, rather than in its own frame.

Usage

# Use only as wrapper of function body, like this:
# my.fun <- function(..., nlocal=sys.parent()) mlocal( expr)
# ...should be replaced by the arguments of "my.fun"
# expr should be replaced by the code of "my.fun"
# nlocal should always be included as shown
mlocal( expr) # Don't use it like this!

Arguments

expr
the function code, normally a braced expression

Value

Details

Sometimes it's useful to write a "child" function that can create and modify variables in its parent directly, without using assign or <<-< code=""> (note that <<-< code=""> will only work on variables that exist already). This can make for clearer, more modular programming; for example, tedious initializations of many variables can be hidden inside an initialize() statement. The definition of an mlocal function does not have to occur within its caller; the mlocal function can exist as a completely separate Robject. mlocal functions can have arguments just like normal functions. These arguments will temporarily hide any objects of the same name in the nlocal frame (i.e. the calling frame). When the mlocal function exits, its arguments will be deleted from the calling frame and the hidden objects (if any) will be restored. Sometimes it's desirable to avoid cluttering the calling frame with variables that only matter to the mlocal function. A useful convention is to "declare" such temporary variables in your function definition, as defaultless arguments after the nlocal argument. The nlocal argument of an mlocal function-- which must ALWAYS be included in the definition, with the default specified as sys.parent()-- can normally be omitted when invoking your mlocal function. However, you will need to set it explicitly when your function is to be called by another, e.g. lapply; see the third example. A more daring usage is to call e.g. fun.mlocal(nlocal=another.frame.number) so that the statements in fun.mlocal get executed in a completely different frame. A convoluted example can be found in the (internal) function find.debug.HQ in the debug package, which creates a frame and defines a large number of variables in it, by calling setup.debug.admin(nlocal=new.frame.number). mlocal functions can be nested, though this gets confusing. By default, all evaluation will happen in the same frame. Note that (at least at present) all arguments are evaluated as soon as your mlocal function is invoked, rather than by the usual lazy evaluation mechanism. Missing arguments are still OK, though. If you call return in an mlocal function, you must call local.return too. Frame-dependent functions (sys.parent()) etc. will not do what you expect inside an mlocal function. In R1.8 at least, you need to shift the normal index by 3 to get what you'd expect, so that sys.call(-3) inside an mlocal function will return the call to the mlocal function, and sys.function( sys.parent(3)) will return the mlocal function definition. You can get the expected results for the caller via mvb.sys.parent with no shift, etc.-- unless the caller is itself an mlocal function. on.exit doesn't work properly, contrary to what previous mlocal documentation said (the scoping is wrong, though often this doesn't matter). If you want to have exit code in the mlocal function itself, use local.on.exit. I can't find any way to set the exit code in the calling function from within an mlocal function. See R-news 2001 #3 (1/3) for another closely related approach to "macros".

See Also

local.return, local.on.exit, do.in.envir, and R-news 1/3

Examples

Run this code
# Tidiness and variable creation
init <- function( nlocal=sys.parent()) mlocal( sqr.a <- a*a)
ffout <- function( a) { init(); sqr.a }
ffout( 5) # 25
# Parameters and temporary variables
ffin <- function( n, nlocal=sys.parent(), a, i) mlocal({
 # this "n" and "a" will temporarily replace caller's "n" and "a"
 print( n)
 a <- 1
 for( i in 1:n)
 a <- a*x
 a
 })
x.to.the.n.plus.1 <- function( x, n) {
 print( ffin( n+1))
 print( n)
 print( ls())
 }
x.to.the.n.plus.1( 3, 2) # prints as follows:
# [1] 3 (in "ffin")
# [1] 27 (result of "ffin")
# [1] 2 (original n)
# [1] "n" "x" (vars in "x.to.the\dots"-- NB no a or i)
# Use of "nlocal"
ffin <- function( i, nlocal=sys.parent()) mlocal( a <- a+i )
ffout <- function( ivec) { a <- 0; sapply( ivec, ffin, nlocal=sys.nframe()) }
ffout( 1:3) # 1 3 6

Run the code above in your browser using DataLab