config Declarations: command-line overrides

The four main declaration keywords that have been introduced so far — var, const, param, and type — support a mode in which their initializing expressions in the source code can be overridden on the command-line. This is achieved by prefixing the declaration with the keyword config. Chapel’s config feature provides a simple means of specifying and overriding default values.

config var and config const

Configuration variables are variables whose default values can be overridden on the executable’s command-line. Here’s a simple example:

examples/users-guide/base/configs.chpl
config var n = 10;

Like a traditional var declaration, this creates a variable named n of type int, initialized to the value 10. However, the config prefix causes the compiler to add a command-line flag to the generated executable that supports overriding that default value. For example, if we run the program as follows:

./configs --n 1000

then n will be initialized to the value 1000 for that execution of the program. The arguments for these flags can also be specified using = (with no spaces) as the separator between the argument name and its value, as follows:

./configs --n=1000

There is no difference between these two forms—it’s simply a matter of taste.

As with normal variables, a config var can be re-assigned throughout its lifetime. In contrast, config const declarations cannot be changed once they are initialized, whether that initial value comes from the source code or the command-line. The following statement declares a pair of configurable constants:

config const epsilon = 0.01,
             debug = false;

As with the previous case, we can override their default values on the executable’s command-line. For instance the following switches to a smaller value of epsilon and flips debug to true:

./configs --epsilon=0.000001 --debug=true

Boolean config declarations, like debug above, also support a mode in which they can be set to true simply by using the flag without a supporting value. Thus, the previous command-line could also have been written:

./configs --epsilon=0.000001 --debug

config param and config type

Chapel also supports configurable param and type declarations via command-line flags. However, since param values and types must be known to the compiler, the default values are overridden using flags on the compiler rather than the generated executable.

As an example, the following statements declare a series of configurable param and type symbols:

config param verbose = false,
             bitMask = 1 << 8,
             size = numBits(int);

config type age = uint(8);

When overriding config declarations in the compiler the -s flag is used to set the new value. As an example, to set verbose to true, we could compile as follows:

chpl -sverbose=true configs2.chpl

or:

chpl -s verbose=true configs2.chpl

As with the generated executable, boolean values can also be set on the compiler’s command line simply using the name of the flag. Thus, the following would be equivalent to the previous line:

chpl -sverbose configs2.chpl

As you’d expect, multiple config declarations can be overridden at once. For example, this command-line overrides the default initializers for bitMask, size, and age:

chpl -sbitMask=0b00001111 -ssize=16 -sage='uint(16)' configs2.chpl

One of the nice things about setting config declarations on the compiler’s command-line is that the specified value can be an arbitrary Chapel expression, provided that the compiler can evaluate it in place of the source-based initialization expression. Thus, we can use more interesting expressions when setting compile-time config values. For example, the following compiler lines specify bitMask, size, and age using expressions other than simple literals and type names:

chpl -sbitMask='1 << 10' -ssize='numBits(bitMask.type)/2' configs2.chpl
chpl -sage='if verbose then uint(32) else uint(16)' configs2.chpl

Note the use of quotes in some of the examples above. These are not required by Chapel itself, but are used to pass nontrivial expressions through the command-line shell (e.g., bash) such that they reach Chapel as a single uninterpreted argument. For example, parenthesis have special meaning for many command-shells, so the use of quotes above tells the shell to ignore them. Other cases use quotes to pass expressions containing whitespace to the Chapel compiler as a single argument. Details about which characters have special meaning and how they can be preserved depend on the specific shell being used and are beyond the scope of this article.

Setting config defaults on the compiler’s command-line also supports more radical alterations to the code. For example, we could change age from an integral type to a floating point type:

--n=1000 --epsilon=0.000000001

Note that config declarations are processed in the order specified by the source code and compiler, not the order that they occur on the command-line. Moreover, the initializing expressions must make sense when considered in the source code context. Thus, the command-line override for a given config can refer to other declarations that precede it in a given module. For instance, we can set age in terms of the config param size since it appeared earlier in the code:

chpl -sage='int(size)'

However, the reverse would not be possible since age has not been initialized when size is declared.

Similarly, subsequent declarations in the source code can themselves be based on config declarations. For instance, here are some declarations that are based on the config param and config type statements shown above and could follow them:

type myInt = int(size);

param sizeOfAge = numBits(age);

Compile-time overrides for config var and config const

Users can also specify default expressions for config var and config const declarations on the compiler’s command-line. As with other compile-time overrides, complex or symbolic expressions can be used since the compiler is involved. As an example, let’s look at a variation on one of our earlier declarations:

config const n = 10,
             epsilon = 0.01;

The following compiler command can be used to override the source code initializer for epsilon so that it will be a function of n by default:

-sepsilon=1.0/n

Such an initializer could not be specified on the executable command-line since the compiler is out of the picture by then. At that point, the values for a config var or config const are restricted to the set of strings that can legally be cast to that type.

Having compiled epsilon to be set in this way, if we specify a value for n on the executable’s command line:

--n=1000

then epsilon will be computed in terms of it, resulting in the value:

0.001

Note that the compiler’s command-line override can still be re-overridden on the executable’s command-line as usual:

--n=1000 --epsilon=0.000000001

Scope of config declarations

Because all config declarations are processed at program startup time, they must appear at module scope. We haven’t talked about modules yet, so for now, think of them as needing to appear as top-level statements in a file.