<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-module-abstractions">
  <title>Abstractions</title>
  <para>
    If you find yourself repeating yourself over and over, it’s time to
    abstract. Take, for instance, this Apache HTTP Server configuration:
  </para>
  <programlisting language="bash">
{
  services.httpd.virtualHosts =
    { &quot;blog.example.org&quot; = {
        documentRoot = &quot;/webroot/blog.example.org&quot;;
        adminAddr = &quot;alice@example.org&quot;;
        forceSSL = true;
        enableACME = true;
        enablePHP = true;
      };
      &quot;wiki.example.org&quot; = {
        documentRoot = &quot;/webroot/wiki.example.org&quot;;
        adminAddr = &quot;alice@example.org&quot;;
        forceSSL = true;
        enableACME = true;
        enablePHP = true;
      };
    };
}
</programlisting>
  <para>
    It defines two virtual hosts with nearly identical configuration;
    the only difference is the document root directories. To prevent
    this duplication, we can use a <literal>let</literal>:
  </para>
  <programlisting language="bash">
let
  commonConfig =
    { adminAddr = &quot;alice@example.org&quot;;
      forceSSL = true;
      enableACME = true;
    };
in
{
  services.httpd.virtualHosts =
    { &quot;blog.example.org&quot; = (commonConfig // { documentRoot = &quot;/webroot/blog.example.org&quot;; });
      &quot;wiki.example.org&quot; = (commonConfig // { documentRoot = &quot;/webroot/wiki.example.com&quot;; });
    };
}
</programlisting>
  <para>
    The <literal>let commonConfig = ...</literal> defines a variable
    named <literal>commonConfig</literal>. The <literal>//</literal>
    operator merges two attribute sets, so the configuration of the
    second virtual host is the set <literal>commonConfig</literal>
    extended with the document root option.
  </para>
  <para>
    You can write a <literal>let</literal> wherever an expression is
    allowed. Thus, you also could have written:
  </para>
  <programlisting language="bash">
{
  services.httpd.virtualHosts =
    let commonConfig = ...; in
    { &quot;blog.example.org&quot; = (commonConfig // { ... })
      &quot;wiki.example.org&quot; = (commonConfig // { ... })
    };
}
</programlisting>
  <para>
    but not <literal>{ let commonConfig = ...; in ...; }</literal> since
    attributes (as opposed to attribute values) are not expressions.
  </para>
  <para>
    <emphasis role="strong">Functions</emphasis> provide another method
    of abstraction. For instance, suppose that we want to generate lots
    of different virtual hosts, all with identical configuration except
    for the document root. This can be done as follows:
  </para>
  <programlisting language="bash">
{
  services.httpd.virtualHosts =
    let
      makeVirtualHost = webroot:
        { documentRoot = webroot;
          adminAddr = &quot;alice@example.org&quot;;
          forceSSL = true;
          enableACME = true;
        };
    in
      { &quot;example.org&quot; = (makeVirtualHost &quot;/webroot/example.org&quot;);
        &quot;example.com&quot; = (makeVirtualHost &quot;/webroot/example.com&quot;);
        &quot;example.gov&quot; = (makeVirtualHost &quot;/webroot/example.gov&quot;);
        &quot;example.nl&quot; = (makeVirtualHost &quot;/webroot/example.nl&quot;);
      };
}
</programlisting>
  <para>
    Here, <literal>makeVirtualHost</literal> is a function that takes a
    single argument <literal>webroot</literal> and returns the
    configuration for a virtual host. That function is then called for
    several names to produce the list of virtual host configurations.
  </para>
</section>
