<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0" xml:id="sec-freeform-modules"> <title>Freeform modules</title> <para> Freeform modules allow you to define values for option paths that have not been declared explicitly. This can be used to add attribute-specific types to what would otherwise have to be <literal>attrsOf</literal> options in order to accept all attribute names. </para> <para> This feature can be enabled by using the attribute <literal>freeformType</literal> to define a freeform type. By doing this, all assignments without an associated option will be merged using the freeform type and combined into the resulting <literal>config</literal> set. Since this feature nullifies name checking for entire option trees, it is only recommended for use in submodules. </para> <example xml:id="ex-freeform-module"> <title>Freeform submodule</title> <para> The following shows a submodule assigning a freeform type that allows arbitrary attributes with <literal>str</literal> values below <literal>settings</literal>, but also declares an option for the <literal>settings.port</literal> attribute to have it type-checked and assign a default value. See <xref linkend="ex-settings-typed-attrs"/> for a more complete example. </para> <programlisting> { lib, config, ... }: { options.settings = lib.mkOption { type = lib.types.submodule { freeformType = with lib.types; attrsOf str; # We want this attribute to be checked for the correct type options.port = lib.mkOption { type = lib.types.port; # Declaring the option also allows defining a default value default = 8080; }; }; }; } </programlisting> <para> And the following shows what such a module then allows </para> <programlisting> { # Not a declared option, but the freeform type allows this settings.logLevel = "debug"; # Not allowed because the the freeform type only allows strings # settings.enable = true; # Allowed because there is a port option declared settings.port = 80; # Not allowed because the port option doesn't allow strings # settings.port = "443"; } </programlisting> </example> <note> <para> Freeform attributes cannot depend on other attributes of the same set without infinite recursion: <programlisting> { # This throws infinite recursion encountered settings.logLevel = lib.mkIf (config.settings.port == 80) "debug"; } </programlisting> To prevent this, declare options for all attributes that need to depend on others. For above example this means to declare <literal>logLevel</literal> to be an option. </para> </note> </section>