# Inheritance and R expressions

## Defaults and inheritance

The `default` configuration provides a set of values to use when no named configuration is active. Other configurations automatically inherit all `default` values so need only define values specialized for that configuration.

For example, in this configuration the `production` configuration doesn’t specify a value for `trials` so it will be read from the `default` configuration:

config.yml

    default:
      trials: 5
      dataset: "data-sampled.csv"

    production:
      dataset: "data.csv"

R

    config::get(config = "production")
    #> $trials
    #> [1] 5
    #>
    #> $dataset
    #> [1] "data.csv"

All configurations automatically inherit from the `default` configuration. Configurations can also inherit from one or more other named configurations. For example, in this file the `production` configuration inherits from the `test` configuration:

config.yml

    default:
      trials: 5
      dataset: "data-sampled.csv"

    test:
      trials: 30
      dataset: "data-test.csv"

    production:
      inherits: test
      dataset: "data.csv"

R

    config::get(config = "production")
    #> $trials
    #> [1] 30
    #>
    #> $inherits
    #> [1] "test"
    #>
    #> $dataset
    #> [1] "data.csv"

## Using R code inside the yaml file

You can execute R code within configuration files by prefacing values with `!expr`. This could be useful in cases where you want to base configuration values on environment variables, R options, or even other config files. For example:

config.yml

    default:
      cores: 2
      debug: true
      server: "localhost:5555"

    production:
      cores: !expr getOption("mc.cores")
      debug: !expr Sys.getenv("ENABLE_DEBUG") == "1"
      server: !expr config::get("server", file = "etc/server-config.yml")

The default result:

R

    config::get("server")
    #> [1] "localhost:5555"

The production result will depend on the environment variables on the server:

server-config.yml

    cat(readLines("etc/server-config.yml"), sep = "\n")
    #> default:
    #>   server: "production.example.com"

R

    config::get("server", config = "production")
    #> [1] "production.example.com"

### Referencing previously assigned parameters

You can use any previously assigned parameter inside of R code so long as it is assigned directly.

config.yml

    default:
      file: "data.csv"

    test:
      data_dir: "test/out"
      dataset: !expr file.path(data_dir, file)

    production:
      data_dir: "production/out"
      dataset: !expr file.path(data_dir, file)

R

    config::get("dataset", config = "test")
    #> [1] "test/out/data.csv"

R

    config::get(config = "production")
    #> $file
    #> [1] "data.csv"
    #>
    #> $data_dir
    #> [1] "production/out"
    #>
    #> $dataset
    #> [1] "production/out/data.csv"

## Limitations of using R expressions

The ability to use R expressions gives substantial flexibility to allow configurations to depend on environment variables, files and other information that’s available in the target environment.

However, keep in mind these limitations:

- Any R expression can only refer to elements in the configuration file that it inherits from.
- An R expression can not refer to another R expression.

If the config file violates these limitations, `config::get()` will throw an error.

As an example, let’s say you want to construct a configuration that computes a location based on a filename and a folder.

### A valid example

If both `filename` and `folder` are constant (i.e. not computed expressions) this works:

config.yml

    default:
      filename: "trials.csv"
      folder: "some/path"
      location: !expr file.path(folder, filename)

R

    config::get("location")
    #> [1] "some/path/trials.csv"

### An invalid example

However, if `folder` is also a computed value, then `location` can not be computed and this throws an error:

config.yml

    default:
      filename: "trials.csv"
      folder: !expr c("some/path")
      location: !expr file.path(folder, filename)

R

    config::get("location")
    #> Error in config::get("location"): Attempt to assign nested list value from expression.
    #> Only directly assigned values can be used in expressions.
    #> Original Error:
    #> *  eval(expr, envir = envir): object 'folder' not found