-
-
Notifications
You must be signed in to change notification settings - Fork 18.1k
Description
Issue description
Options being allowed to be named config and options causes problems related to module syntax. The canonical module syntax has a number of sections, mainly config, options, imports, disabledModules and freeformType, which are the only allowed top-level attributes. This might look like
{
imports = [ ... ];
options = ...;
config = ...;
# Not allowed
foo = ...;
}But another syntax, sometimes called shorthand syntax, is also supported, which triggers when neither options nor config is set. In this alternate syntax, setting arbitrary attributes is valid, with the semantics that they get put into the config section. This might look like this:
{
imports = [ ... ];
# options, config <- needs to not be defined
foo = ...;
}This is then equivalent to
{
imports = [ ... ];
config.foo = ...;
}If you don't need any option definitions, this syntax is more convenient.
The problem arises when you have options with names matching the section names, such as config, options, imports, or others. Assuming we have an option named options, then we can't use the convenient shorthand syntax:
{
# Since `options` is declared, the canonical syntax is used, not the shorthand one,
# making this be an option declaration, not a definition!
options = ...;
}To actually declare this option you need to use
{
config.options = ...;
}Similarly, if you have an option named config, you can't do
{
config = ...;
}but instead need to write
{
config.config = ...;
}If options have a name matching another section name, like imports or disallowedModules, shorthand syntax also can't be used, because these attributes are interpreted as sections, not options definitions. E.g. with an option named imports, the following doesn't work:
{
imports = ...;
}Instead this is needed:
{
config.imports = ...;
}These same problems are also caused by submodules, except that there it's even more nuanced, because submodules declared with types.submodule historically have always forced the shorthand syntax by wrapping its definitions into a config value. This was done to prevent surprises when trying to set a config option. E.g. if you have an option foo of type types.submodule, which itself has a nested config option, then you can do this:
{
foo.config = ...;
}Instead of having to do this:
{
foo.config.config = ...;
}But the flip side is that there's no way to then declare non-config sections. The only known hack around this is to declare values as a function, because only attribute values are wrapped under config (since anything else wouldn't be valid as av config value):
{
foo = { ... }: {
options = ...;
};
}With the introduction of types.submoduleWith, a parameter shorthandOnlyDefinesConfig was introduced, which controls whether this config wrapping is done, defaulting to it not being done (in contrast with types.submodule)
Suggested solution
By disallowing options to be named according to a section name, we can always know whether an attribute is a section or an option name, allowing us to avoid all these problems:
- No more weird edge cases regarding shorthand syntax: Every option can be defined using shorthand syntax.
- The whole
configwrapping fortypes.submoduleandtypes.submoduleWithcan be removed, cleaning up code and removing the need for function hacks. - We should also change the rule of "
optionsorconfigbeing set implies canonical syntax" to just "configbeing set implies canonical syntax". If options can't be namedoptions, then there's no reason options couldn't be declared using anoptionsattribute while using shorthand syntax forconfig.
The hardest part is backwards compatibility and transition, because in NixOS there are a whole bunch of options named config and some named options (and one named imports). Another section name that could be problematic is key.
We should also consider whether it's possible to rename config and options to something else. I've heard complaints in the past that these have been confusing.
Metadata
Metadata
Assignees
Labels
Projects
Status