2014-08-24 18:18:18 +01:00
|
|
|
|
<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-module-abstractions">
|
2018-05-02 00:57:09 +01:00
|
|
|
|
<title>Abstractions</title>
|
2014-08-24 18:18:18 +01:00
|
|
|
|
|
2018-05-02 00:57:09 +01:00
|
|
|
|
<para>
|
|
|
|
|
If you find yourself repeating yourself over and over, it’s time to
|
|
|
|
|
abstract. Take, for instance, this Apache HTTP Server configuration:
|
2014-08-24 18:18:18 +01:00
|
|
|
|
<programlisting>
|
|
|
|
|
{
|
2018-04-05 09:43:56 +01:00
|
|
|
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
2014-08-24 18:18:18 +01:00
|
|
|
|
[ { hostName = "example.org";
|
|
|
|
|
documentRoot = "/webroot";
|
|
|
|
|
adminAddr = "alice@example.org";
|
|
|
|
|
enableUserDir = true;
|
|
|
|
|
}
|
|
|
|
|
{ hostName = "example.org";
|
|
|
|
|
documentRoot = "/webroot";
|
|
|
|
|
adminAddr = "alice@example.org";
|
|
|
|
|
enableUserDir = true;
|
|
|
|
|
enableSSL = true;
|
|
|
|
|
sslServerCert = "/root/ssl-example-org.crt";
|
|
|
|
|
sslServerKey = "/root/ssl-example-org.key";
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
2018-05-02 00:57:09 +01:00
|
|
|
|
It defines two virtual hosts with nearly identical configuration; the only
|
|
|
|
|
difference is that the second one has SSL enabled. To prevent this
|
|
|
|
|
duplication, we can use a <literal>let</literal>:
|
2014-08-24 18:18:18 +01:00
|
|
|
|
<programlisting>
|
|
|
|
|
let
|
|
|
|
|
exampleOrgCommon =
|
|
|
|
|
{ hostName = "example.org";
|
|
|
|
|
documentRoot = "/webroot";
|
|
|
|
|
adminAddr = "alice@example.org";
|
|
|
|
|
enableUserDir = true;
|
|
|
|
|
};
|
|
|
|
|
in
|
|
|
|
|
{
|
2018-04-05 09:43:56 +01:00
|
|
|
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
2014-08-24 18:18:18 +01:00
|
|
|
|
[ exampleOrgCommon
|
|
|
|
|
(exampleOrgCommon // {
|
|
|
|
|
enableSSL = true;
|
|
|
|
|
sslServerCert = "/root/ssl-example-org.crt";
|
|
|
|
|
sslServerKey = "/root/ssl-example-org.key";
|
|
|
|
|
})
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
2018-05-02 00:57:09 +01:00
|
|
|
|
The <literal>let exampleOrgCommon = <replaceable>...</replaceable></literal>
|
|
|
|
|
defines a variable named <literal>exampleOrgCommon</literal>. The
|
|
|
|
|
<literal>//</literal> operator merges two attribute sets, so the
|
|
|
|
|
configuration of the second virtual host is the set
|
|
|
|
|
<literal>exampleOrgCommon</literal> extended with the SSL options.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
You can write a <literal>let</literal> wherever an expression is allowed.
|
|
|
|
|
Thus, you also could have written:
|
2014-08-24 18:18:18 +01:00
|
|
|
|
<programlisting>
|
|
|
|
|
{
|
2018-04-05 09:43:56 +01:00
|
|
|
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
2014-08-24 18:18:18 +01:00
|
|
|
|
let exampleOrgCommon = <replaceable>...</replaceable>; in
|
|
|
|
|
[ exampleOrgCommon
|
|
|
|
|
(exampleOrgCommon // { <replaceable>...</replaceable> })
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
2018-05-02 00:57:09 +01:00
|
|
|
|
but not <literal>{ let exampleOrgCommon = <replaceable>...</replaceable>; in
|
|
|
|
|
<replaceable>...</replaceable>; }</literal> since attributes (as opposed to
|
|
|
|
|
attribute values) are not expressions.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
<emphasis>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 host name. This can be done
|
|
|
|
|
as follows:
|
2014-08-24 18:18:18 +01:00
|
|
|
|
<programlisting>
|
|
|
|
|
{
|
2018-04-05 09:43:56 +01:00
|
|
|
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
2014-08-24 18:18:18 +01:00
|
|
|
|
let
|
|
|
|
|
makeVirtualHost = name:
|
|
|
|
|
{ hostName = name;
|
|
|
|
|
documentRoot = "/webroot";
|
|
|
|
|
adminAddr = "alice@example.org";
|
|
|
|
|
};
|
|
|
|
|
in
|
|
|
|
|
[ (makeVirtualHost "example.org")
|
|
|
|
|
(makeVirtualHost "example.com")
|
|
|
|
|
(makeVirtualHost "example.gov")
|
|
|
|
|
(makeVirtualHost "example.nl")
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
2018-05-02 00:57:09 +01:00
|
|
|
|
Here, <varname>makeVirtualHost</varname> is a function that takes a single
|
|
|
|
|
argument <literal>name</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>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
We can further improve on this by using the function <varname>map</varname>,
|
|
|
|
|
which applies another function to every element in a list:
|
2014-08-24 18:18:18 +01:00
|
|
|
|
<programlisting>
|
|
|
|
|
{
|
2018-04-05 09:43:56 +01:00
|
|
|
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
2014-08-24 18:18:18 +01:00
|
|
|
|
let
|
|
|
|
|
makeVirtualHost = <replaceable>...</replaceable>;
|
|
|
|
|
in map makeVirtualHost
|
|
|
|
|
[ "example.org" "example.com" "example.gov" "example.nl" ];
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
2018-05-02 00:57:09 +01:00
|
|
|
|
(The function <literal>map</literal> is called a <emphasis>higher-order
|
|
|
|
|
function</emphasis> because it takes another function as an argument.)
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
What if you need more than one argument, for instance, if we want to use a
|
|
|
|
|
different <literal>documentRoot</literal> for each virtual host? Then we can
|
|
|
|
|
make <varname>makeVirtualHost</varname> a function that takes a
|
|
|
|
|
<emphasis>set</emphasis> as its argument, like this:
|
2014-08-24 18:18:18 +01:00
|
|
|
|
<programlisting>
|
|
|
|
|
{
|
2018-04-05 09:43:56 +01:00
|
|
|
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
2014-08-24 18:18:18 +01:00
|
|
|
|
let
|
|
|
|
|
makeVirtualHost = { name, root }:
|
|
|
|
|
{ hostName = name;
|
|
|
|
|
documentRoot = root;
|
|
|
|
|
adminAddr = "alice@example.org";
|
|
|
|
|
};
|
|
|
|
|
in map makeVirtualHost
|
|
|
|
|
[ { name = "example.org"; root = "/sites/example.org"; }
|
|
|
|
|
{ name = "example.com"; root = "/sites/example.com"; }
|
|
|
|
|
{ name = "example.gov"; root = "/sites/example.gov"; }
|
|
|
|
|
{ name = "example.nl"; root = "/sites/example.nl"; }
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
2018-05-02 00:57:09 +01:00
|
|
|
|
But in this case (where every root is a subdirectory of
|
|
|
|
|
<filename>/sites</filename> named after the virtual host), it would have been
|
|
|
|
|
shorter to define <varname>makeVirtualHost</varname> as
|
2014-08-24 18:18:18 +01:00
|
|
|
|
<programlisting>
|
|
|
|
|
makeVirtualHost = name:
|
|
|
|
|
{ hostName = name;
|
|
|
|
|
documentRoot = "/sites/${name}";
|
|
|
|
|
adminAddr = "alice@example.org";
|
|
|
|
|
};
|
|
|
|
|
</programlisting>
|
2018-05-02 00:57:09 +01:00
|
|
|
|
Here, the construct <literal>${<replaceable>...</replaceable>}</literal>
|
|
|
|
|
allows the result of an expression to be spliced into a string.
|
|
|
|
|
</para>
|
2014-08-24 18:18:18 +01:00
|
|
|
|
</section>
|