This allows optionally not having a definition at all, which makes sure to use the default instead. E.g.
{ config, ... }: {
value = mkIf config.enable {
foo = mkIf config.enableFoo "foo";
};
}
# turns into (wich enable = true and enableFoo = false)
{
value = {};
}
Option and type merging
Options can be defined multiple times with different types, which are then merged together. Useful for submodule modularity and changing submodule defaults. This is used for the fileSystems NixOS option. E.g. this allows splitting modules into backend-specific settings:
{
options.value = mkOption {
type = attrsOf (submodule ({ config, ... }: {
options = {
result = mkOption {
type = lines;
};
backend = mkOption {
type = enum [];
};
shared = mkOption {
type = types.str;
};
};
config.result = ''
shared = ${config.shared};
'';
}));
};
}
# Another module (and can be many more like this)
{
options.value = mkOption {
type = attrsOf (submodule ({ config, ... }: {
options.backend = mkOption {
type = enum [ "foo" ];
default = "foo";
};
options.foo.fooSpecific = mkOption {
type = str;
};
config.result = mkIf (config.backend == "foo") ''
foo = ${config.foo.fooSpecific}
'';
}));
};
}
Custom types
lib.types provides a bunch of ready-made types, but users can also define their own, with custom checks and merging strategies. See also https://github.com/NixOS/nixpkgs/pull/75584
{
options.smolString = mkOption {
type = types.addCheck (s: builtins.stringLength s < 32) types.str;
};
options.funType = mkOption {
type = types.mkOptionType {
check = builtins.isFunction;
# Merge functions by joining the attributes they create
merge = loc: defs: arg: foldl' (l: r: l // r) {} (map (d: d.value arg) defs);
};
};
}
Freeform modules
Recently introduced freeform modules add the ability to not have to declare all options, providing a fallback type for all definitions not matched to an option, which gets merged into the result. Very useful for not having to declare every option of package settings, while still having custom type checking for some of them:
{
options.settings = mkOption {
default = {};
type = with types; submodule {
# All values that don't have an associated option have to be of this type
freeformType = attrsOf int;
options.port = mkOption {
type = port;
default = 80;
description = "The port to use.";
};
};
};
# Works, even though foo isn't declared as an option
config.settings.foo = 10;
# error: The option value `settings.port' in `example.nix' is not of type
# `16 bit unsigned integer; between 0 and 65535 (both inclusive)'.
#config.settings.port = "not-a-port";
## Overrides default value
config.settings.port = 8080;
}
Disabling of modules
If a modules is included by default, it can be removed with disabledModules:
{
disabledModules = [
# Disable because it slows things down
"tasks/lvm.nix"
# Disable because I want to provide my own services.murmur.* set
"services/networking/murmur.nix"
];
}
Read-only options
Aka, only allow 1 definition, which is usually declared by the module that sets read-only directly. This is usually used for exposing module evaluation results.
{
options.value = mkOption {
readOnly = true;
# We set it here, nobody else can set it now
default = "value";
};
}
Type coercion
This is really just another custom type, but its implementation allows converting one definition type into another one. Very useful for backwards compatibility
{
# First version
options.value = mkOption {
type = str;
};
# Backwards-compatible second version
options.value = mkOption {
type = coercedTo str (s: { name = s; }) (submodule {
options.name = mkOption { type = str; };
options.other = mkOption { /* ... */ };
});
};
}
Inspecting options
Including their type, defaults, definitions, etc., and using them for new options or configs:
Aliased packages and package sets
as I understand what functor does is object closure ( eg summing some integers ), I am not sure how that helps with overriding args of the function
-- noob
Module system features and use-cases for them
Priority-based merging
mkDefault
,mkForce
, etc. Also works recursively. This allows precise control over how defaults are persisted or overridden. E.g.Conditional definitions using
mkIf
This allows optionally not having a definition at all, which makes sure to use the default instead. E.g.
Option and type merging
Options can be defined multiple times with different types, which are then merged together. Useful for submodule modularity and changing submodule defaults. This is used for the fileSystems NixOS option. E.g. this allows splitting modules into backend-specific settings:
Custom types
lib.types
provides a bunch of ready-made types, but users can also define their own, with custom checks and merging strategies. See also https://github.com/NixOS/nixpkgs/pull/75584Freeform modules
Recently introduced freeform modules add the ability to not have to declare all options, providing a fallback type for all definitions not matched to an option, which gets merged into the result. Very useful for not having to declare every option of package settings, while still having custom type checking for some of them:
Disabling of modules
If a modules is included by default, it can be removed with
disabledModules
:Read-only options
Aka, only allow 1 definition, which is usually declared by the module that sets read-only directly. This is usually used for exposing module evaluation results.
Type coercion
This is really just another custom type, but its implementation allows converting one definition type into another one. Very useful for backwards compatibility
Inspecting options
Including their type, defaults, definitions, etc., and using them for new options or configs:
Priority-based definition ordering
Using
mkBefore
,mkAfter
, and co. This is mostly useful for specifying element orders in lists. But it could also be used for e.g. overlay order.Overlay/overrides features and use-cases of them
Overriding package sets
Such that package changes propagate to all the dependents of it
Overriding callPackage arguments per-package
Changing the callPackage arguments for just a single package, without influencing others:
Similarly with
.override
Overriding package sets per-package
If a package and all its dependencies need a different version, this could be done with override like this:
Which is very painful, and incorrect if you missed a dependency. Some package sets (like the haskell one) provide
.overrideScope
for this purpose:See also https://github.com/NixOS/nixpkgs/pull/67422.
pkgs.appendOverlays
andpkgs.extend
work like this as well. Also, this can also work on package sets themselves, e.g.Overriding mkDerivation, derivation, and other function arguments
See also https://github.com/NixOS/rfcs/pull/67
Aliased packages and package sets
This should ideally be disallowed, but this is currently a thing:
Overriding function arguments
Possible using __functor: