<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" 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>
  <anchor xml:id="ex-freeform-module" />
  <para>
    <emphasis role="strong">Example: Freeform submodule</emphasis>
  </para>
  <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
    <link linkend="ex-settings-typed-attrs">Example: Declaring a
    type-checked <literal>settings</literal> attribute</link> for a more
    complete example.
  </para>
  <programlisting language="bash">
{ 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 language="bash">
{
  # Not a declared option, but the freeform type allows this
  settings.logLevel = &quot;debug&quot;;

  # 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 = &quot;443&quot;;
}
</programlisting>
  <note>
    <para>
      Freeform attributes cannot depend on other attributes of the same
      set without infinite recursion:
    </para>
    <programlisting language="bash">
{
  # This throws infinite recursion encountered
  settings.logLevel = lib.mkIf (config.settings.port == 80) &quot;debug&quot;;
}
</programlisting>
    <para>
      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>
