# Configuration files for R projects

ronfig aims to enable intuitive configuration of R projects via a single function, `load_config()`. It’s raison d’être is to handle the situation where you have multiple, somewhat overlapping, parameter configurations that you want to quickly switch between whilst avoiding, potentially error-inducing, copy and paste.

To keep things simple, rather than introducing another language for our configuration file, we restrict ourselves to a subset of base R. By default we allow the following set of functions/operators:

- `<-`, `=`, `+`, `-`, `*`, `:`
- `$`, `[`, `[[`
- `$<-`, `[<-`, `[[<-`
- `c()`
- `as.Date()`
- `array()`, `matrix()`
- `list()`, `data.frame()`
- `Sys.Date()`, `Sys.time()`
- `seq()`, `sequence()`, and `seq_len()`
- `file.path()`
- `modifyList()`

We also define a convenience function, `cc()`, which automatically quotes input to save typing.

This use of a reduced subset of R is primarily to ensure that loading a configuration file has no side-effects on a users environment and performs limited computation. Under the hood, `load_config()` is little more than careful wrapping of `base::sys.source()` and `utils::modifyList()` with care taken as to where evaluation takes place.

Should you want more flexibility, the argument `strict = FALSE` can be set. This enables all functions from base R. In particular, you can then use `::` to call in to other packages.

## Example

### Configuration file

Configuration files look similar to the following example file that we bundle in the package:

    file <- system.file("config.R", package = "ronfig")
    cat(readChar(file, file.info(file)$size))

    # This is our default configuration which must be a named list
    # All other changes are layered on top of this.
    default <- list(
        date = as.Date("2025-09-02"),
        N = 1000,
        alpha = 0.3,
        gamma = 0.2,
        beta = 0.7,
        max_delay = 30,
        years = 2006:2025
    )

    # You may need to debug some results and wish to set one parameter to 0 and only
    # look at a reduced number of years, e.g.
    debug <- list(
        years = 2024:2025,
        alpha = 0
    )

    # Or you may wish to consider an extended range of years
    forecast <- list(
        years = 2006:2030
    )

### Loading

We can easily load any of the example configurations in to a list in our local environment:

    library(ronfig)
    str(load_config(file))

    #> List of 7
    #>  $ date     : Date[1:1], format: "2025-09-02"
    #>  $ N        : num 1000
    #>  $ alpha    : num 0.3
    #>  $ gamma    : num 0.2
    #>  $ beta     : num 0.7
    #>  $ max_delay: num 30
    #>  $ years    : int [1:20] 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 ...

    str(load_config(file, "debug"))

    #> List of 7
    #>  $ date     : Date[1:1], format: "2025-09-02"
    #>  $ N        : num 1000
    #>  $ alpha    : num 0
    #>  $ gamma    : num 0.2
    #>  $ beta     : num 0.7
    #>  $ max_delay: num 30
    #>  $ years    : int [1:2] 2024 2025

    str(load_config(file, "forecast"))

    #> List of 7
    #>  $ date     : Date[1:1], format: "2025-09-02"
    #>  $ N        : num 1000
    #>  $ alpha    : num 0.3
    #>  $ gamma    : num 0.2
    #>  $ beta     : num 0.7
    #>  $ max_delay: num 30
    #>  $ years    : int [1:25] 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 ...

### Loading things as they are

If we just wish to load the configuration file as is, we can pass the argument `as_is = TRUE`. This will load configuration file in to a list. If we specify a specific configuration, that value will be pulled out and returned directly:

    str(load_config(file, as_is = TRUE))

    #> List of 3
    #>  $ forecast:List of 1
    #>   ..$ years: int [1:25] 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 ...
    #>  $ default :List of 7
    #>   ..$ date     : Date[1:1], format: "2025-09-02"
    #>   ..$ N        : num 1000
    #>   ..$ alpha    : num 0.3
    #>   ..$ gamma    : num 0.2
    #>   ..$ beta     : num 0.7
    #>   ..$ max_delay: num 30
    #>   ..$ years    : int [1:20] 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 ...
    #>  $ debug   :List of 2
    #>   ..$ years: int [1:2] 2024 2025
    #>   ..$ alpha: num 0

    str(load_config(file, "forecast", as_is = TRUE))

    #> List of 1
    #>  $ years: int [1:25] 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 ...

### Using other functions

To make other functions available within the configuration file, users can inject `carrier::crate()` objects in to the evaluation environment. As an example, assume we wanted to permit the use of `mean()` within our configuration file. By default this would error:

    f <- tempfile()
    cat("default <- list(a=mean(1:10))", file = f)
    with(load_config(f), a)

    #> Error in `load_config()`:
    #> ! Unable to load configuration file: could not find function "mean"
    #> ℹ Only the following functions are available to use in rconfig files:
    #> • <-, =, +, -, *, :
    #> • as.Date
    #> • array, matrix
    #> • list, data.frame
    #> • c, cc
    #> • [, [[, $
    #> • $<-, [<-
    #> • Sys.Date, Sys.time
    #> • seq, sequence, seq_len
    #> • file.path
    #> • modifyList

If we now inject `mean()` as a crated function, things work as desired:

    crate <- carrier::crate(function(x) mean(x))
    with(load_config(f, crates = list(mean = crate)), a)

    #> [1] 5.5

    unlink(f)

## Related work

- config which uses yaml for it’s configuration file.