R: EXPERIMENTAL: Turn a function into a model macro builder A...

model_macro_builder {nimble} | R Documentation

## EXPERIMENTAL: Turn a function into a model macro builder A model macro expands one line of code in a nimbleModel into one or more new lines. This supports compact programming by defining re-usable modules. `model_macro_builder` takes as input a function that constructs new lines of model code from the original line of code. It returns a function suitable for internal use by `nimbleModel` that arranges arguments for input function. Macros are an experimental feature and are available only after setting `nimbleOptions(enableModelMacros = TRUE)`.

### Description

EXPERIMENTAL: Turn a function into a model macro builder A model macro expands one line of code in a nimbleModel into one or more new lines. This supports compact programming by defining re-usable modules. `model_macro_builder` takes as input a function that constructs new lines of model code from the original line of code. It returns a function suitable for internal use by `nimbleModel` that arranges arguments for input function. Macros are an experimental feature and are available only after setting `nimbleOptions(enableModelMacros = TRUE)`.

### Usage

    model_macro_builder(fun, use3pieces = TRUE, unpackArgs = TRUE)

### Arguments

`fun` | A function written to construct new lines of model code.
`use3pieces` | (TRUE or FALSE) Should the arguments from the input line be split into pieces for the LHS (left-hand side), RHS (right-hand side, possibly further split depending on `unpackArgs`), and `stoch` (TRUE if the line uses a `~`, FALSE otherwise)? See details and examples.
`unpackArgs` | (TRUE or FALSE) Should arguments be passed as a list (FALSE) or as separate arguments (TRUE)? See details and examples.

### Details

The arguments `use3pieces` and `unpackArgs` indicate how `fun` expects to have arguments arranged from an input line of code (processed by `nimbleModel`).

Consider the defaults `use3pieces = TRUE` and `unpackArgs = TRUE`, for a macro called `macro1`. In this case, the line of model code `x ~ macro1(arg1 = z[1:10], arg2 = "hello")` will be passed to `fun` as `fun(stoch = TRUE, LHS = x, arg1 = z[1:10], arg2 = "hello")`.

If `use3pieces = TRUE` but `unpackArgs = FALSE`, then the RHS will be passed as is, without unpacking its arguments into separate arguments to `fun`. In this case, `x ~ macro1(arg1 = z[1:10], arg2 = "hello")` will be passed to `fun` as `fun(stoch = TRUE, LHS = x, RHS = macro1(arg1 = z[1:10], arg2 = "hello"))`.

If `use3pieces = FALSE` and `unpackArgs = FALSE`, the entire line of code is passed as a single object. In this case, `x ~ macro1(arg1 = z[1:10], arg2 = "hello")` will be passed to `fun` as `fun(x ~ macro1(arg1 = z[1:10], arg2 = "hello"))`. It is also possible in this case to pass a macro without using a `~` or `<-`. For example, the line `macro1(arg1 = z[1:10], arg2 = "hello")` will be passed to `fun` as `fun(macro1(arg1 = z[1:10], arg2 = "hello"))`.

If `use3pieces = FALSE` and `unpackArgs = TRUE`, it won't make sense to anticipate a declaration using `~` or `<-`. Ins#' tead, arguments from an arbitrary call will be passed as separate arguments. #' For example, the line `macro1(arg1 = z[1:10], arg2 = "hello")` will be pa#' ssed to `fun` as `fun(arg1 = z[1:10], arg2 = "hello")`.

It is extremely useful to be familiar with processing R code as an object to write `fun` correctly. Functions such as `substitute` and `as.name` (e.g. `as.name('~')`), `quote`, `parse` and `deparse` are particularly handy.

Multiple lines of new code should be contained in `{}` . Extra curly braces are not a problem. See example 2.

Macro expansion is done recursively: One macro can return code that invokes another macro.

### Value

A list with a named element `code` that contains the replacement code.

### Examples

    nimbleOptions(enableModelMacros = TRUE)
    nimbleOptions(verbose = FALSE)

    ## Example 1: Say one is tired of writing "for" loops.
    ## This macro will generate a "for" loop with dnorm declarations
    all_dnorm <- model_macro_builder(
        function(stoch, LHS, RHSvar, start, end, sd = 1) {
            newCode <- substitute(
                for(i in START:END) {
                    LHS[i] ~ dnorm(RHSvar[i], SD)
                },
                list(START = start,
                     END = end,
                     LHS = LHS,
                     RHSvar = RHSvar,
                     SD = sd))
            list(code = newCode)
        },
        use3pieces = TRUE,
        unpackArgs = TRUE
    )

    model1 <- nimbleModel(
        nimbleCode(
        {
            ## Create a "for" loop of dnorm declarations by invoking the macro
            x ~ all_dnorm(mu, start = 1, end = 10)
        }
        ))

    ## show code from expansion of macro
    model1$getCode()
    ## The result should be:
    ## {
    ##     for (i in 1:10) {
    ##         x[i] ~ dnorm(mu[i], 1)
    ##     }
    ## }

    ## Example 2: Say one is tired of writing priors.
    ## This macro will generate a set of priors in one statement
    flat_normal_priors <- model_macro_builder(
        function(...) {
            allVars <- list(...)
            priorDeclarations <- lapply(allVars,
                                        function(x)
                                            substitute(VAR ~ dnorm(0, sd = 1000),
                                                       list(VAR = x)))
            newCode <- quote({})
            newCode[2:(length(allVars)+1)] <- priorDeclarations
            list(code = newCode)
        },
        use3pieces = FALSE,
        unpackArgs = TRUE
    )

    model2 <- nimbleModel(
        nimbleCode(
        {
            flat_normal_priors(mu, beta, gamma)
        }
        ))

    ## show code from expansion of macro
    model2$getCode()
    ## The result should be:
    ## {
    ##     {
    ##         mu ~ dnorm(0, sd = 1000)
    ##         beta ~ dnorm(0, sd = 1000)
    ##         gamma ~ dnorm(0, sd = 1000)
    ##     }
    ## }
    ## Extra curly braces do not matter.

[Package nimble version 0.9.1 Index]