at 18.09-beta 5.4 kB view raw
1<section xmlns="http://docbook.org/ns/docbook" 2 xmlns:xlink="http://www.w3.org/1999/xlink" 3 xmlns:xi="http://www.w3.org/2001/XInclude" 4 version="5.0" 5 xml:id="sec-module-abstractions"> 6 <title>Abstractions</title> 7 8 <para> 9 If you find yourself repeating yourself over and over, it’s time to 10 abstract. Take, for instance, this Apache HTTP Server configuration: 11<programlisting> 12{ 13 <xref linkend="opt-services.httpd.virtualHosts"/> = 14 [ { hostName = "example.org"; 15 documentRoot = "/webroot"; 16 adminAddr = "alice@example.org"; 17 enableUserDir = true; 18 } 19 { hostName = "example.org"; 20 documentRoot = "/webroot"; 21 adminAddr = "alice@example.org"; 22 enableUserDir = true; 23 enableSSL = true; 24 sslServerCert = "/root/ssl-example-org.crt"; 25 sslServerKey = "/root/ssl-example-org.key"; 26 } 27 ]; 28} 29</programlisting> 30 It defines two virtual hosts with nearly identical configuration; the only 31 difference is that the second one has SSL enabled. To prevent this 32 duplication, we can use a <literal>let</literal>: 33<programlisting> 34let 35 exampleOrgCommon = 36 { hostName = "example.org"; 37 documentRoot = "/webroot"; 38 adminAddr = "alice@example.org"; 39 enableUserDir = true; 40 }; 41in 42{ 43 <xref linkend="opt-services.httpd.virtualHosts"/> = 44 [ exampleOrgCommon 45 (exampleOrgCommon // { 46 enableSSL = true; 47 sslServerCert = "/root/ssl-example-org.crt"; 48 sslServerKey = "/root/ssl-example-org.key"; 49 }) 50 ]; 51} 52</programlisting> 53 The <literal>let exampleOrgCommon = <replaceable>...</replaceable></literal> 54 defines a variable named <literal>exampleOrgCommon</literal>. The 55 <literal>//</literal> operator merges two attribute sets, so the 56 configuration of the second virtual host is the set 57 <literal>exampleOrgCommon</literal> extended with the SSL options. 58 </para> 59 60 <para> 61 You can write a <literal>let</literal> wherever an expression is allowed. 62 Thus, you also could have written: 63<programlisting> 64{ 65 <xref linkend="opt-services.httpd.virtualHosts"/> = 66 let exampleOrgCommon = <replaceable>...</replaceable>; in 67 [ exampleOrgCommon 68 (exampleOrgCommon // { <replaceable>...</replaceable> }) 69 ]; 70} 71</programlisting> 72 but not <literal>{ let exampleOrgCommon = <replaceable>...</replaceable>; in 73 <replaceable>...</replaceable>; }</literal> since attributes (as opposed to 74 attribute values) are not expressions. 75 </para> 76 77 <para> 78 <emphasis>Functions</emphasis> provide another method of abstraction. For 79 instance, suppose that we want to generate lots of different virtual hosts, 80 all with identical configuration except for the host name. This can be done 81 as follows: 82<programlisting> 83{ 84 <xref linkend="opt-services.httpd.virtualHosts"/> = 85 let 86 makeVirtualHost = name: 87 { hostName = name; 88 documentRoot = "/webroot"; 89 adminAddr = "alice@example.org"; 90 }; 91 in 92 [ (makeVirtualHost "example.org") 93 (makeVirtualHost "example.com") 94 (makeVirtualHost "example.gov") 95 (makeVirtualHost "example.nl") 96 ]; 97} 98</programlisting> 99 Here, <varname>makeVirtualHost</varname> is a function that takes a single 100 argument <literal>name</literal> and returns the configuration for a virtual 101 host. That function is then called for several names to produce the list of 102 virtual host configurations. 103 </para> 104 105 <para> 106 We can further improve on this by using the function <varname>map</varname>, 107 which applies another function to every element in a list: 108<programlisting> 109{ 110 <xref linkend="opt-services.httpd.virtualHosts"/> = 111 let 112 makeVirtualHost = <replaceable>...</replaceable>; 113 in map makeVirtualHost 114 [ "example.org" "example.com" "example.gov" "example.nl" ]; 115} 116</programlisting> 117 (The function <literal>map</literal> is called a <emphasis>higher-order 118 function</emphasis> because it takes another function as an argument.) 119 </para> 120 121 <para> 122 What if you need more than one argument, for instance, if we want to use a 123 different <literal>documentRoot</literal> for each virtual host? Then we can 124 make <varname>makeVirtualHost</varname> a function that takes a 125 <emphasis>set</emphasis> as its argument, like this: 126<programlisting> 127{ 128 <xref linkend="opt-services.httpd.virtualHosts"/> = 129 let 130 makeVirtualHost = { name, root }: 131 { hostName = name; 132 documentRoot = root; 133 adminAddr = "alice@example.org"; 134 }; 135 in map makeVirtualHost 136 [ { name = "example.org"; root = "/sites/example.org"; } 137 { name = "example.com"; root = "/sites/example.com"; } 138 { name = "example.gov"; root = "/sites/example.gov"; } 139 { name = "example.nl"; root = "/sites/example.nl"; } 140 ]; 141} 142</programlisting> 143 But in this case (where every root is a subdirectory of 144 <filename>/sites</filename> named after the virtual host), it would have been 145 shorter to define <varname>makeVirtualHost</varname> as 146<programlisting> 147makeVirtualHost = name: 148 { hostName = name; 149 documentRoot = "/sites/${name}"; 150 adminAddr = "alice@example.org"; 151 }; 152</programlisting> 153 Here, the construct <literal>${<replaceable>...</replaceable>}</literal> 154 allows the result of an expression to be spliced into a string. 155 </para> 156</section>