167 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			XML
		
	
	
	
	
	
		
		
			
		
	
	
			167 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			XML
		
	
	
	
	
	
| 
								 | 
							
								<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">
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<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:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<programlisting>
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								  services.httpd.virtualHosts =
							 | 
						|||
| 
								 | 
							
								    [ { 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>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								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>:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<programlisting>
							 | 
						|||
| 
								 | 
							
								let
							 | 
						|||
| 
								 | 
							
								  exampleOrgCommon =
							 | 
						|||
| 
								 | 
							
								    { hostName = "example.org";
							 | 
						|||
| 
								 | 
							
								      documentRoot = "/webroot";
							 | 
						|||
| 
								 | 
							
								      adminAddr = "alice@example.org";
							 | 
						|||
| 
								 | 
							
								      enableUserDir = true;
							 | 
						|||
| 
								 | 
							
								    };
							 | 
						|||
| 
								 | 
							
								in
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								  services.httpd.virtualHosts =
							 | 
						|||
| 
								 | 
							
								    [ exampleOrgCommon
							 | 
						|||
| 
								 | 
							
								      (exampleOrgCommon // {
							 | 
						|||
| 
								 | 
							
								        enableSSL = true;
							 | 
						|||
| 
								 | 
							
								        sslServerCert = "/root/ssl-example-org.crt";
							 | 
						|||
| 
								 | 
							
								        sslServerKey = "/root/ssl-example-org.key";
							 | 
						|||
| 
								 | 
							
								      })
							 | 
						|||
| 
								 | 
							
								    ];
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								</programlisting>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								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:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<programlisting>
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								  services.httpd.virtualHosts =
							 | 
						|||
| 
								 | 
							
								    let exampleOrgCommon = <replaceable>...</replaceable>; in
							 | 
						|||
| 
								 | 
							
								    [ exampleOrgCommon
							 | 
						|||
| 
								 | 
							
								      (exampleOrgCommon // { <replaceable>...</replaceable> })
							 | 
						|||
| 
								 | 
							
								    ];
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								</programlisting>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								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:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<programlisting>
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								  services.httpd.virtualHosts =
							 | 
						|||
| 
								 | 
							
								    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>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								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:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<programlisting>
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								  services.httpd.virtualHosts =
							 | 
						|||
| 
								 | 
							
								    let
							 | 
						|||
| 
								 | 
							
								      makeVirtualHost = <replaceable>...</replaceable>;
							 | 
						|||
| 
								 | 
							
								    in map makeVirtualHost
							 | 
						|||
| 
								 | 
							
								      [ "example.org" "example.com" "example.gov" "example.nl" ];
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								</programlisting>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								(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:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<programlisting>
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								  services.httpd.virtualHosts =
							 | 
						|||
| 
								 | 
							
								    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>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								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
							 | 
						|||
| 
								 | 
							
								<programlisting>
							 | 
						|||
| 
								 | 
							
								makeVirtualHost = name:
							 | 
						|||
| 
								 | 
							
								  { hostName = name;
							 | 
						|||
| 
								 | 
							
								    documentRoot = "/sites/${name}";
							 | 
						|||
| 
								 | 
							
								    adminAddr = "alice@example.org";
							 | 
						|||
| 
								 | 
							
								  };
							 | 
						|||
| 
								 | 
							
								</programlisting>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Here, the construct
							 | 
						|||
| 
								 | 
							
								<literal>${<replaceable>...</replaceable>}</literal> allows the result
							 | 
						|||
| 
								 | 
							
								of an expression to be spliced into a string.</para>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								</section>
							 |