Problem

Sometimes we want to pass config maps to certain functions. Handling default config values is a breeze with the use of Map.merge/2.

For example, we have a function that accepts data and config:

def process(data, config \\ %{}) do
    data
    |> Task.async_stream(
        &sort/1,
        ordered: config.ordered,
        max_concurrency: config.max_concurrency
    )
    |> Stream.run()
end

What if it's called with insufficient config? For example, %{max_concurrency: 10} only. This would result to KeyError since ordered is not found.

Solution

To solve this, we can create a module attribute that has the default config.

@default_config %{ordered: false, max_concurrency: System.schedulers_online()}

And then we merge our config variable (config that would override the defaults) to the @default_config.

merged_config = Map.merge(@default_config, config)

# => %{ordered: false, max_concurrency: 10}

Simply put, keys of config (second argument) have precedence over @default_config (first argument).