1<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-module-abstractions">
2 <title>Abstractions</title>
3 <para>
4 If you find yourself repeating yourself over and over, it’s time to
5 abstract. Take, for instance, this Apache HTTP Server configuration:
6 </para>
7 <programlisting language="bash">
8{
9 services.httpd.virtualHosts =
10 { "blog.example.org" = {
11 documentRoot = "/webroot/blog.example.org";
12 adminAddr = "alice@example.org";
13 forceSSL = true;
14 enableACME = true;
15 enablePHP = true;
16 };
17 "wiki.example.org" = {
18 documentRoot = "/webroot/wiki.example.org";
19 adminAddr = "alice@example.org";
20 forceSSL = true;
21 enableACME = true;
22 enablePHP = true;
23 };
24 };
25}
26</programlisting>
27 <para>
28 It defines two virtual hosts with nearly identical configuration;
29 the only difference is the document root directories. To prevent
30 this duplication, we can use a <literal>let</literal>:
31 </para>
32 <programlisting language="bash">
33let
34 commonConfig =
35 { adminAddr = "alice@example.org";
36 forceSSL = true;
37 enableACME = true;
38 };
39in
40{
41 services.httpd.virtualHosts =
42 { "blog.example.org" = (commonConfig // { documentRoot = "/webroot/blog.example.org"; });
43 "wiki.example.org" = (commonConfig // { documentRoot = "/webroot/wiki.example.com"; });
44 };
45}
46</programlisting>
47 <para>
48 The <literal>let commonConfig = ...</literal> defines a variable
49 named <literal>commonConfig</literal>. The <literal>//</literal>
50 operator merges two attribute sets, so the configuration of the
51 second virtual host is the set <literal>commonConfig</literal>
52 extended with the document root option.
53 </para>
54 <para>
55 You can write a <literal>let</literal> wherever an expression is
56 allowed. Thus, you also could have written:
57 </para>
58 <programlisting language="bash">
59{
60 services.httpd.virtualHosts =
61 let commonConfig = ...; in
62 { "blog.example.org" = (commonConfig // { ... })
63 "wiki.example.org" = (commonConfig // { ... })
64 };
65}
66</programlisting>
67 <para>
68 but not <literal>{ let commonConfig = ...; in ...; }</literal> since
69 attributes (as opposed to attribute values) are not expressions.
70 </para>
71 <para>
72 <emphasis role="strong">Functions</emphasis> provide another method
73 of abstraction. For instance, suppose that we want to generate lots
74 of different virtual hosts, all with identical configuration except
75 for the document root. This can be done as follows:
76 </para>
77 <programlisting language="bash">
78{
79 services.httpd.virtualHosts =
80 let
81 makeVirtualHost = webroot:
82 { documentRoot = webroot;
83 adminAddr = "alice@example.org";
84 forceSSL = true;
85 enableACME = true;
86 };
87 in
88 { "example.org" = (makeVirtualHost "/webroot/example.org");
89 "example.com" = (makeVirtualHost "/webroot/example.com");
90 "example.gov" = (makeVirtualHost "/webroot/example.gov");
91 "example.nl" = (makeVirtualHost "/webroot/example.nl");
92 };
93}
94</programlisting>
95 <para>
96 Here, <literal>makeVirtualHost</literal> is a function that takes a
97 single argument <literal>webroot</literal> and returns the
98 configuration for a virtual host. That function is then called for
99 several names to produce the list of virtual host configurations.
100 </para>
101</section>