<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-option-declarations">
 <title>Option Declarations</title>

 <para>
  An option declaration specifies the name, type and description of a NixOS
  configuration option. It is invalid to define an option that hasn’t been
  declared in any module. An option declaration generally looks like this:
<programlisting>
options = {
  <replaceable>name</replaceable> = mkOption {
    type = <replaceable>type specification</replaceable>;
    default = <replaceable>default value</replaceable>;
    example = <replaceable>example value</replaceable>;
    description = "<replaceable>Description for use in the NixOS manual.</replaceable>";
  };
};
</programlisting>
  The attribute names within the <replaceable>name</replaceable> attribute path
  must be camel cased in general but should, as an exception, match the
  <link
xlink:href="https://nixos.org/nixpkgs/manual/#sec-package-naming">
  package attribute name</link> when referencing a Nixpkgs package. For
  example, the option <varname>services.nix-serve.bindAddress</varname>
  references the <varname>nix-serve</varname> Nixpkgs package.
 </para>

 <para>
  The function <varname>mkOption</varname> accepts the following arguments.
  <variablelist>
   <varlistentry>
    <term>
     <varname>type</varname>
    </term>
    <listitem>
     <para>
      The type of the option (see <xref linkend='sec-option-types' />). It may
      be omitted, but that’s not advisable since it may lead to errors that
      are hard to diagnose.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term>
     <varname>default</varname>
    </term>
    <listitem>
     <para>
      The default value used if no value is defined by any module. A default is
      not required; but if a default is not given, then users of the module
      will have to define the value of the option, otherwise an error will be
      thrown.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term>
     <varname>example</varname>
    </term>
    <listitem>
     <para>
      An example value that will be shown in the NixOS manual.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term>
     <varname>description</varname>
    </term>
    <listitem>
     <para>
      A textual description of the option, in DocBook format, that will be
      included in the NixOS manual.
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </para>

 <section xml:id="sec-option-declarations-eot">
  <title>Extensible Option Types</title>

  <para>
   Extensible option types is a feature that allow to extend certain types
   declaration through multiple module files. This feature only work with a
   restricted set of types, namely <literal>enum</literal> and
   <literal>submodules</literal> and any composed forms of them.
  </para>

  <para>
   Extensible option types can be used for <literal>enum</literal> options that
   affects multiple modules, or as an alternative to related
   <literal>enable</literal> options.
  </para>

  <para>
   As an example, we will take the case of display managers. There is a central
   display manager module for generic display manager options and a module file
   per display manager backend (slim, sddm, gdm ...).
  </para>

  <para>
   There are two approach to this module structure:
   <itemizedlist>
    <listitem>
     <para>
      Managing the display managers independently by adding an enable option to
      every display manager module backend. (NixOS)
     </para>
    </listitem>
    <listitem>
     <para>
      Managing the display managers in the central module by adding an option
      to select which display manager backend to use.
     </para>
    </listitem>
   </itemizedlist>
  </para>

  <para>
   Both approaches have problems.
  </para>

  <para>
   Making backends independent can quickly become hard to manage. For display
   managers, there can be only one enabled at a time, but the type system can
   not enforce this restriction as there is no relation between each backend
   <literal>enable</literal> option. As a result, this restriction has to be
   done explicitely by adding assertions in each display manager backend
   module.
  </para>

  <para>
   On the other hand, managing the display managers backends in the central
   module will require to change the central module option every time a new
   backend is added or removed.
  </para>

  <para>
   By using extensible option types, it is possible to create a placeholder
   option in the central module
   (<xref linkend='ex-option-declaration-eot-service'
      />), and to extend
   it in each backend module
   (<xref
      linkend='ex-option-declaration-eot-backend-slim' />,
   <xref
      linkend='ex-option-declaration-eot-backend-sddm' />).
  </para>

  <para>
   As a result, <literal>displayManager.enable</literal> option values can be
   added without changing the main service module file and the type system
   automatically enforce that there can only be a single display manager
   enabled.
  </para>

  <example xml:id='ex-option-declaration-eot-service'>
   <title>Extensible type placeholder in the service module</title>
<screen>
services.xserver.displayManager.enable = mkOption {
  description = "Display manager to use";
  type = with types; nullOr (enum [ ]);
};</screen>
  </example>

  <example xml:id='ex-option-declaration-eot-backend-slim'>
   <title>Extending <literal>services.xserver.displayManager.enable</literal> in the <literal>slim</literal> module</title>
<screen>
services.xserver.displayManager.enable = mkOption {
  type = with types; nullOr (enum [ "slim" ]);
};</screen>
  </example>

  <example xml:id='ex-option-declaration-eot-backend-sddm'>
   <title>Extending <literal>services.xserver.displayManager.enable</literal> in the <literal>sddm</literal> module</title>
<screen>
services.xserver.displayManager.enable = mkOption {
  type = with types; nullOr (enum [ "sddm" ]);
};</screen>
  </example>

  <para>
   The placeholder declaration is a standard <literal>mkOption</literal>
   declaration, but it is important that extensible option declarations only
   use the <literal>type</literal> argument.
  </para>

  <para>
   Extensible option types work with any of the composed variants of
   <literal>enum</literal> such as <literal>with types; nullOr (enum [ "foo"
   "bar" ])</literal> or <literal>with types; listOf (enum [ "foo" "bar"
   ])</literal>.
  </para>
 </section>
</section>
