-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
367 lines (366 loc) · 47 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Mostly Absurd</title>
<link href="https://mostlyabsurd.github.io/atom.xml" rel="self" />
<link href="https://mostlyabsurd.github.io" />
<id>https://mostlyabsurd.github.io/atom.xml</id>
<author>
<name>Robert Djubek</name>
<email>[email protected]</email>
</author>
<updated>2019-09-15T00:00:00Z</updated>
<entry>
<title>Min-Maxing Your Haskell Imports (examples)</title>
<link href="https://mostlyabsurd.github.io/posts/2019/06/09/minimal-imports-maximal-imports-examples/" />
<id>https://mostlyabsurd.github.io/posts/2019/06/09/minimal-imports-maximal-imports-examples/</id>
<published>2019-06-09T00:00:00Z</published>
<updated>2019-09-15T00:00:00Z</updated>
<summary type="html"><div class="row">
<div class="col-md-8 offset-md-2 col-sm-10 offset-sm-1 p-md-1 p-4">
<section class="header">
<div class="col-md-6 offset-md-3 col-sm-8 offset-sm-2 p-1">
<h6><span>Posted on June 9, 2019 by Robert Djubek </span></h6>
</div>
<div class="col-md-6 offset-md-3 col-sm-8 offset-sm-2 p-1">
<h6><span class="blog-tag"> <a href="/tags/haskell/">haskell</a>, <a href="/tags/vim/">vim</a>, <a href="/tags/tooling/">tooling</a>, and <a href="/tags/nixos/">nixos</a> </span></h6>
</div>
</section>
<div>
<br />
<br />
<section>
<h2 id="real-examples-of-usage-and-rationale">Real Examples of Usage and Rationale<a href="#real-examples-of-usage-and-rationale" class="anchor"> #</a></h2>
<p>In the previous post there were not a lot of examples of what the programs actually do or how to use them effectively. Here you will find more helpful information (hopefully). I will also attempt to explain reasons you should use minimal imports in your programs.</p>
<h3 id="maximal-imports">Maximal imports<a href="#maximal-imports" class="anchor"> #</a></h3>
<p>We’ll start with the simpler maximal-imports program. The effect on the imports is small but it does not require any changes to work.</p>
<h4 id="an-actual-maximal-imports.hs">An actual maximal-imports.hs<a href="#an-actual-maximal-imports.hs" class="anchor"> #</a></h4>
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource haskell numberLines"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1"></a><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="kw">module</span> <span class="dt">Main</span> <span class="kw">where</span></span>
<span id="cb1-4"><a href="#cb1-4"></a></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="kw">import</span> <span class="dt">Data.Text</span> (<span class="dt">Text</span>)</span>
<span id="cb1-7"><a href="#cb1-7"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span></span>
<span id="cb1-8"><a href="#cb1-8"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span></span>
<span id="cb1-9"><a href="#cb1-9"></a></span>
<span id="cb1-10"><a href="#cb1-10"></a><span class="ot">filterImports ::</span> <span class="dt">Text</span> <span class="ot">-></span> <span class="dt">Maybe</span> <span class="dt">Text</span></span>
<span id="cb1-11"><a href="#cb1-11"></a>filterImports t <span class="ot">=</span></span>
<span id="cb1-12"><a href="#cb1-12"></a> <span class="kw">let</span> a <span class="ot">=</span> <span class="fu">fst</span> <span class="op">$</span> Data.Text.breakOn <span class="st">"("</span> t</span>
<span id="cb1-13"><a href="#cb1-13"></a> <span class="kw">in</span> <span class="kw">if</span> Data.Text.isPrefixOf <span class="st">"import"</span> a</span>
<span id="cb1-14"><a href="#cb1-14"></a> <span class="kw">then</span> <span class="dt">Just</span> <span class="op">$</span> Data.Text.strip a</span>
<span id="cb1-15"><a href="#cb1-15"></a> <span class="kw">else</span> <span class="dt">Nothing</span></span>
<span id="cb1-16"><a href="#cb1-16"></a></span>
<span id="cb1-17"><a href="#cb1-17"></a><span class="ot">adjustText ::</span> <span class="dt">Text</span> <span class="ot">-></span> <span class="dt">Text</span></span>
<span id="cb1-18"><a href="#cb1-18"></a>adjustText oldText <span class="ot">=</span> newText</span>
<span id="cb1-19"><a href="#cb1-19"></a> <span class="kw">where</span></span>
<span id="cb1-20"><a href="#cb1-20"></a> oldLines <span class="ot">=</span> Data.Text.lines oldText</span>
<span id="cb1-21"><a href="#cb1-21"></a> newLines <span class="ot">=</span> mapMaybe filterImports oldLines</span>
<span id="cb1-22"><a href="#cb1-22"></a> newText <span class="ot">=</span> Data.Text.unlines newLines</span>
<span id="cb1-23"><a href="#cb1-23"></a></span>
<span id="cb1-24"><a href="#cb1-24"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb1-25"><a href="#cb1-25"></a>main <span class="ot">=</span> Data.Text.IO.interact adjustText</span></code></pre></div>
<h4 id="example-no.-1">Example no. 1<a href="#example-no.-1" class="anchor"> #</a></h4>
<p>As you can see it is quite simple. Notice line 6 which reads <code>import Data.Text (Text)</code></p>
<p>Let’s run the program on itself and see what happens.</p>
<p>To do this we use vim (or in emacs evil mode or your editors equivalent)</p>
<p>Make sure the program is compiled:</p>
<pre><code>ghc -O2 maximal-imports.hs</code></pre>
<pre><code>vim maximal-imports.hs</code></pre>
<p>In visual line mode (V) select these lines:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Text</span> (<span class="dt">Text</span>)</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span></span></code></pre></div>
<p>Still in visual mode type</p>
<pre><code>:!./maximal-imports</code></pre>
<p>If you have put it on your path you may ommit the <code>./</code></p>
<p>Your imports will now read as follows:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Text</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span></span></code></pre></div>
<p>See the difference? <code>import Data.Text (Text)</code> has been changed to <code>import Data.Text</code></p>
<p>This is not a large change and it may not be obvious why you would want to do this. It’s a trivial change to make if you had reason to so why do we need a program? Why would we even want to make the change in the first place? First, Let’s see the effect on a different set of imports. The following is from a minimal-imports.hs.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co">-- Main.imports</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span> (isJust, mapMaybe)</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Text</span> (<span class="dt">Text</span>)</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a> ( breakOnEnd</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a> , isPrefixOf</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a> , isSuffixOf</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a> , <span class="fu">lines</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a> , <span class="fu">pack</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a> , strip</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a> , unpack</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a> )</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span> (getContents, putStrLn, readFile)</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Safe</span> (headMay)</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.Directory</span> (getCurrentDirectory)</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.Directory.Extra</span> (listFilesRecursive)</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.FilePath</span> (takeFileName)</span></code></pre></div>
<p>After we complete the same process of opening in vim, selecting the imports, and running <code>:!maximal-imports</code> (assuming it’s on your path this time) we end up with this:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Text</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Safe</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.Directory</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.Directory.Extra</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.FilePath</span></span></code></pre></div>
<p>This is a much more drastic change! As you can guess, that would take a bit longer to do by hand. If you were doing this often to files with many imports it would be tedious. (Thus the creation of these programs) It’s possible your program will not compile immediately after this. If more than one module imports something named the same you’ll get conflicts. All you need to do is modify the problematic import statements to resolve the conflict (Use qualified or explicit imports) This is actually one of the reasons to use minimal imports in the first place… see Rationale near end.</p>
<h4 id="why">Why?<a href="#why" class="anchor"> #</a></h4>
<p>The original form with all of the imports listed explicitly is ideal. That’s what we want the final result to look like. The downside is when the imports are in that form it makes it harder to develop. You constantly have to modify the imports when you want to use another function or data type from a module. Your editor might not provide as useful automatic completion if the whole module is not in scope. GHC will complain. Your program won’t compile without adding imports. It might at least suggest what you need to import but then you have to move to the top of the file and edit the list and break your workflow. It’s much easier to temporarily relax the imports while adding features and then go back to minimal (explicit) imports when you’re finished.</p>
<h5 id="thats-great-but">That’s great, but…<a href="#thats-great-but" class="anchor"> #</a></h5>
<p>I know, I know, now that you’ve removed all of the explicit imports, you have to add them back… which is a lot of extra work there wasn’t before. That’s why I made another program that does the opposite. :)</p>
<h2 id="minimal-imports">Minimal imports<a href="#minimal-imports" class="anchor"> #</a></h2>
<h3 id="prerequisites">Prerequisites<a href="#prerequisites" class="anchor"> #</a></h3>
<p>The maximal-imports program works without having to change any part of your process. You don’t have to modify compiler flags, you don’t have to add any comments; it just works. minimal-imports requires a tiny bit of setup on your part. You need to add 1 comment and enable and use the ghc flag -ddump-minimal-imports. You can do this with ghc, ghcid, or add it to your cabal file <code>ghc-options</code>, or however you add ghc flags to your build system.</p>
<p>The comment you need to add is of the form <code>-- M.imports</code> and it’s best to put this above your imports. Make sure to select it along with your imports when you run the program. (in the previous example it was <code>-- Main.imports</code> It’s the full name of your module. <code>-- Foo.Bar.imports</code> for a module in <code>src/Foo/Bar.hs</code> GHC will create a file with the required information. minimal-imports will use the comment to find the right file.</p>
<p>To summarize:</p>
<ul>
<li>enable and compile your program with the GHC flag -ddump-minimal-imports</li>
<li>have a comment of the form <code>-- Module.Name.imports</code> that you select</li>
<li>use program the same as maximal-imports except instead type <code>:!minimal-imports</code></li>
</ul>
<p>We can compile the program (shown later):</p>
<pre><code>ghc -O2 -ddump-minimal-imports minimal-imports.hs</code></pre>
<p>It is important to have compiled your program more recently than you’ve last added any imports or you will not get correct results. This is why ghcid is useful here. You could do something like this:</p>
<pre><code>ghcid --command="ghci -ddump-minimal-imports minimal-imports.hs"</code></pre>
<p>I highly recommend ghcid in general but it is especially useful here.</p>
<p>If you forget to select the comment or if the file GHC creates can’t be found (maybe you forgot to enable the flag, or compile, or typed the module name wrong) you should end up with unchanged imports. If something bad happens make sure sure you can undo somehow. Nothing bad has happened yet but… the program is still new and unproven. :)</p>
<h4 id="an-actual-minimal-imports.hs">An actual minimal-imports.hs<a href="#an-actual-minimal-imports.hs" class="anchor"> #</a></h4>
<div class="sourceCode" id="cb11"><pre class="sourceCode numberSource haskell numberLines"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1"></a><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></span>
<span id="cb11-2"><a href="#cb11-2"></a></span>
<span id="cb11-3"><a href="#cb11-3"></a><span class="kw">module</span> <span class="dt">Main</span> <span class="kw">where</span></span>
<span id="cb11-4"><a href="#cb11-4"></a></span>
<span id="cb11-5"><a href="#cb11-5"></a><span class="co">-- Main.imports</span></span>
<span id="cb11-6"><a href="#cb11-6"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span> (isJust, mapMaybe)</span>
<span id="cb11-7"><a href="#cb11-7"></a><span class="kw">import</span> <span class="dt">Data.Text</span> (<span class="dt">Text</span>)</span>
<span id="cb11-8"><a href="#cb11-8"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span></span>
<span id="cb11-9"><a href="#cb11-9"></a> ( breakOnEnd</span>
<span id="cb11-10"><a href="#cb11-10"></a> , isPrefixOf</span>
<span id="cb11-11"><a href="#cb11-11"></a> , isSuffixOf</span>
<span id="cb11-12"><a href="#cb11-12"></a> , <span class="fu">lines</span></span>
<span id="cb11-13"><a href="#cb11-13"></a> , <span class="fu">pack</span></span>
<span id="cb11-14"><a href="#cb11-14"></a> , strip</span>
<span id="cb11-15"><a href="#cb11-15"></a> , unpack</span>
<span id="cb11-16"><a href="#cb11-16"></a> )</span>
<span id="cb11-17"><a href="#cb11-17"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span> (getContents, putStrLn, readFile)</span>
<span id="cb11-18"><a href="#cb11-18"></a><span class="kw">import</span> <span class="dt">Safe</span> (headMay)</span>
<span id="cb11-19"><a href="#cb11-19"></a><span class="kw">import</span> <span class="dt">System.Directory</span> (getCurrentDirectory)</span>
<span id="cb11-20"><a href="#cb11-20"></a><span class="kw">import</span> <span class="dt">System.Directory.Extra</span> (listFilesRecursive)</span>
<span id="cb11-21"><a href="#cb11-21"></a><span class="kw">import</span> <span class="dt">System.FilePath</span> (takeFileName)</span>
<span id="cb11-22"><a href="#cb11-22"></a></span>
<span id="cb11-23"><a href="#cb11-23"></a><span class="ot">findFile ::</span> (<span class="dt">FilePath</span> <span class="ot">-></span> <span class="dt">Bool</span>) <span class="ot">-></span> <span class="dt">FilePath</span> <span class="ot">-></span> <span class="dt">IO</span> (<span class="dt">Maybe</span> <span class="dt">FilePath</span>)</span>
<span id="cb11-24"><a href="#cb11-24"></a>findFile p path <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb11-25"><a href="#cb11-25"></a> names <span class="ot"><-</span> listFilesRecursive path</span>
<span id="cb11-26"><a href="#cb11-26"></a> <span class="fu">return</span> <span class="op">$</span> headMay (<span class="fu">filter</span> p names)</span>
<span id="cb11-27"><a href="#cb11-27"></a></span>
<span id="cb11-28"><a href="#cb11-28"></a><span class="ot">findImportsFile ::</span> <span class="dt">Text</span> <span class="ot">-></span> <span class="dt">IO</span> (<span class="dt">Maybe</span> <span class="dt">FilePath</span>)</span>
<span id="cb11-29"><a href="#cb11-29"></a>findImportsFile f <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb11-30"><a href="#cb11-30"></a> cd <span class="ot"><-</span> getCurrentDirectory</span>
<span id="cb11-31"><a href="#cb11-31"></a> findFile (\x <span class="ot">-></span> takeFileName x <span class="op">==</span> (Data.Text.unpack<span class="ot"> f ::</span> <span class="dt">FilePath</span>)) cd</span>
<span id="cb11-32"><a href="#cb11-32"></a></span>
<span id="cb11-33"><a href="#cb11-33"></a><span class="ot">filterImportsName ::</span> <span class="dt">Text</span> <span class="ot">-></span> <span class="dt">Maybe</span> <span class="dt">Text</span></span>
<span id="cb11-34"><a href="#cb11-34"></a>filterImportsName t <span class="ot">=</span> name</span>
<span id="cb11-35"><a href="#cb11-35"></a> <span class="kw">where</span></span>
<span id="cb11-36"><a href="#cb11-36"></a> isComment <span class="ot">=</span> Data.Text.isPrefixOf <span class="st">"--"</span> t</span>
<span id="cb11-37"><a href="#cb11-37"></a> isImport <span class="ot">=</span> Data.Text.isSuffixOf <span class="st">".imports"</span> t</span>
<span id="cb11-38"><a href="#cb11-38"></a> name <span class="ot">=</span></span>
<span id="cb11-39"><a href="#cb11-39"></a> <span class="kw">if</span> isComment <span class="op">&&</span> isImport</span>
<span id="cb11-40"><a href="#cb11-40"></a> <span class="kw">then</span> <span class="dt">Just</span> <span class="op">$</span> Data.Text.strip <span class="op">$</span> <span class="fu">snd</span> <span class="op">$</span> Data.Text.breakOnEnd <span class="st">"--"</span> t</span>
<span id="cb11-41"><a href="#cb11-41"></a> <span class="kw">else</span> <span class="dt">Nothing</span></span>
<span id="cb11-42"><a href="#cb11-42"></a></span>
<span id="cb11-43"><a href="#cb11-43"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb11-44"><a href="#cb11-44"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb11-45"><a href="#cb11-45"></a> input <span class="ot"><-</span> Data.Text.IO.getContents</span>
<span id="cb11-46"><a href="#cb11-46"></a> <span class="kw">let</span> importFileName <span class="ot">=</span> mapMaybe filterImportsName <span class="op">$</span> Data.Text.lines input</span>
<span id="cb11-47"><a href="#cb11-47"></a> importFile <span class="ot"><-</span></span>
<span id="cb11-48"><a href="#cb11-48"></a> <span class="kw">case</span> headMay importFileName <span class="kw">of</span></span>
<span id="cb11-49"><a href="#cb11-49"></a> <span class="dt">Nothing</span> <span class="ot">-></span> <span class="fu">return</span> <span class="dt">Nothing</span></span>
<span id="cb11-50"><a href="#cb11-50"></a> <span class="dt">Just</span> file <span class="ot">-></span> findImportsFile file</span>
<span id="cb11-51"><a href="#cb11-51"></a> importContents <span class="ot"><-</span></span>
<span id="cb11-52"><a href="#cb11-52"></a> <span class="kw">case</span> importFile <span class="kw">of</span></span>
<span id="cb11-53"><a href="#cb11-53"></a> <span class="dt">Nothing</span> <span class="ot">-></span> <span class="fu">return</span> input</span>
<span id="cb11-54"><a href="#cb11-54"></a> <span class="dt">Just</span> file <span class="ot">-></span> <span class="kw">do</span></span>
<span id="cb11-55"><a href="#cb11-55"></a> x <span class="ot"><-</span> Data.Text.IO.readFile file</span>
<span id="cb11-56"><a href="#cb11-56"></a> <span class="fu">return</span> <span class="op">$</span> <span class="st">"-- "</span> <span class="op"><></span> Data.Text.pack (takeFileName file) <span class="op"><></span> <span class="st">"\n"</span> <span class="op"><></span> x</span>
<span id="cb11-57"><a href="#cb11-57"></a> <span class="kw">let</span> output <span class="ot">=</span> importContents</span>
<span id="cb11-58"><a href="#cb11-58"></a> Data.Text.IO.putStrLn output</span></code></pre></div>
<h4 id="example-no.-2">Example no. 2<a href="#example-no.-2" class="anchor"> #</a></h4>
<p>The above program is the source of minimal-imports.hs after running minimal-imports on what looked more like the previous example of maximal-imports:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Maybe</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Data.Text</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text.IO</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Safe</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.Directory</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.Directory.Extra</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">System.FilePath</span></span></code></pre></div>
<p>(The complete program has been formatted by hindent, the exact version before formatting may sometimes be on fewer lines but should otherwise be verbatim)</p>
<h2 id="rationale">Rationale<a href="#rationale" class="anchor"> #</a></h2>
<h3 id="pvp-bounds">PVP Bounds<a href="#pvp-bounds" class="anchor"> #</a></h3>
<p>If you use explicit/minimal imports you’re more resilient when it comes updates of your program dependencies. It’s a lot less likely an update to a module will break your program by introducing nameclashes. You can use major verison upper bounds instead of only minor version upper bounds (more) safely. Because you’re explicitly importing only the things you need a module can’t add something that conflicts with your other imports or your own program. This is a good thing and it’s expected you to do this.</p>
<h3 id="more-maintainable">More Maintainable<a href="#more-maintainable" class="anchor"> #</a></h3>
<p>When you can look at the program imports and see exactly what module something is being brought into scope from it makes it a lot easier to understand and maintain your codebase. As well as helping avoid potential nameclashes. It just makes sense to do it.</p>
<h3 id="you-were-already-doing-it">You Were Already Doing It<a href="#you-were-already-doing-it" class="anchor"> #</a></h3>
<p>This isn’t anything new. All it does is automate a process that you were (or should have) been already doing. Normally you would enable the GHC flag and copy paste if you wanted the compiler to do most of the work. Many people do it by hand. Both of these are somewhat error prone, tedious and time consuming. This helps make it a bit easier. I also had fun writing it… but mostly I was tired of doing it by hand to this very site. I needed to have <em>something</em> to write posts about, didn’t I?</p>
</section>
</div>
</div>
</div>
</summary>
</entry>
<entry>
<title>Min-Maxing Your Haskell Imports</title>
<link href="https://mostlyabsurd.github.io/posts/2019/06/03/minimal-imports-maximal-imports/" />
<id>https://mostlyabsurd.github.io/posts/2019/06/03/minimal-imports-maximal-imports/</id>
<published>2019-06-03T00:00:00Z</published>
<updated>2019-09-15T00:00:00Z</updated>
<summary type="html"><div class="row">
<div class="col-md-8 offset-md-2 col-sm-10 offset-sm-1 p-md-1 p-4">
<section class="header">
<div class="col-md-6 offset-md-3 col-sm-8 offset-sm-2 p-1">
<h6><span>Posted on June 3, 2019 by Robert Djubek </span></h6>
</div>
<div class="col-md-6 offset-md-3 col-sm-8 offset-sm-2 p-1">
<h6><span class="blog-tag"> <a href="/tags/haskell/">haskell</a>, <a href="/tags/vim/">vim</a>, <a href="/tags/tooling/">tooling</a>, and <a href="/tags/nixos/">nixos</a> </span></h6>
</div>
</section>
<div>
<br />
<br />
<section>
<h2 id="explicit-imports">Explicit imports<a href="#explicit-imports" class="anchor"> #</a></h2>
<p>As you may well know it’s good practice when writing haskell to use qualified imports when appropriate as well as explicitly list what you import and export from <a href="https://en.wikibooks.org/wiki/Haskell/Modules">modules.</a> This is beneficial for someone else (which includes your future self) to understand where the functions and data types you’re using originate. It makes your code more approachable and maintanable. The problem is when you’re originally writing (or editing later) the code you don’t know what you’re going to need! This can be annoying.</p>
<p>How do we have our cake and eat it too? When we’re writing software we want easy access to imports (for both you and your editor for the sake of code completion), and when it’s “done” to use explicit imports… until the next time we work on it. ;)</p>
<h3 id="editors-are-your-friends">Editors are your friends<a href="#editors-are-your-friends" class="anchor"> #</a></h3>
<p>If you’re using an IDE or a proper editor (vim, emacs, etc.) with appropriate plugins ( <a href="https://github.com/ndmitchell/ghcid">ghcid</a>, <a href="https://github.com/chrisdone/hindent">hindent</a>, <a href="https://github.com/ndmitchell/hlint">hlint</a> ), various compiler flags, … your tools can and will help you with this problem. They might suggest what you need to import, or inform you of unnecessary imports you aren’t using. You’ll get various warnings and compile errors that prompt you along. You can also scan the code yourself and decide. You can start off by importing a whole module, and then adding () and fixing compile errors 1 by 1 adding to the list. Anyway, it’s tedious. Who has time for that?</p>
<h3 id="more-tools-to-make-writing-idiomatic-code-easier">More tools to make writing idiomatic code easier<a href="#more-tools-to-make-writing-idiomatic-code-easier" class="anchor"> #</a></h3>
<p>I’m a tool maker. I like tools. I especially like <a href="https://en.wikipedia.org/wiki/Tool_(band)">Tool</a>. I might even be a tool. Most importantly I like making tools and having tools. I like fixing things and being efficient. and I’m a recovering perfectionist. If I can make a tool (either software or in meatspace) that makes a task easier, better, faster… <a href="https://xkcd.com/1319/">I will.</a> Sometimes the tool takes longer to make than the task to complete. However, the joy for me is often in making and using the tool. The tool that <em>I</em> made. Why does this matter to you? I made a few useful tools to further aid in solving the above problem. and I’m sharing them with you.</p>
<h2 id="enter-minimal-imports-and-maximal-imports">Enter minimal-imports and maximal-imports<a href="#enter-minimal-imports-and-maximal-imports" class="anchor"> #</a></h2>
<p>I.e., the inspiration of the title of this post. and this post. Yes, it’s an absurd title. Look where you are. :) Anyway, “Minimal imports” is actually a real thing that I didn’t totally just make up. Maximal imports (the name), however, is a joke. The corresponding program is serious. Naming is hard. Even <a href="https://twitter.com/kmett">Edward Kmett</a> didn’t have a name for the concept that maximal-imports embodies.</p>
<p><a href="https://gist.github.com/Kiwi/5e8ce26a765720d6116e1fe572acf712">This</a> is what you’ve been eagerly anticipating while reading this robbish. There you will (currently) find two files. They will eventually be in a normal git repository. I have a shell.nix I use to setup the environment and a few other things I want to add. In the meantime you can compile them <code>ghc -O2 filename.hs</code> and put them on your path. If you find problems let me know and I’ll try to fix. They’re quite primitive and unrefined at this point. They’re probably not even good. I hacked them together over “a few” sleep deprived hours over night, got them to work, and that’s about where they are right now. Then again, what they do isn’t particularly complicated, either. Ironically they’re probably not idiomatic. But the imports serve as examples of the to and from.</p>
<h3 id="how-its-used">How it’s used<a href="#how-its-used" class="anchor"> #</a></h3>
<p>In your proper editor (vim, doom-emacs, spacemacs, evil-mode, …) go into visual mode, select all of your imports, only your imports (with 2 caveats), and type one of</p>
<pre><code>:!minimal-imports
:!maximal-imports</code></pre>
<p>make sure you have saved recently in case something goes wrong… it shouldn’t go wrong, but… there are no guarantees, there is no warranty. :)</p>
<p>Depending on which one you use you will either end up with your imports replaced with the minimal amount of imports that explicitly list everything, or maximal imports that import entire modules. You may also end up with no modifications to your imports in certain circumstances (see the caveats) Maximal-imports can potentially cause problems if shadowing occurs between modules. Just fix that import (use qualified imports or what not) if GHC complains.</p>
<p>This is surely possible to use in a subset of other editors but I’m not sure how to because I don’t use them. If you figure it out let me know and I’ll update the instructions.</p>
<h3 id="how-it-works">How it works<a href="#how-it-works" class="anchor"> #</a></h3>
<p>The vim command sends the selected text to the program over stdin/stdout. The program sends text back after modifying it. Vim replaces the text you selected with the text the program sends over the pipe. The program does a bit of hackish checking to determine what is an import and what is not. Minimal-imports will attempt to find a file that contains the necessary imports. Explained below.</p>
<h2 id="how-to-use">How to use<a href="#how-to-use" class="anchor"> #</a></h2>
<p><strong>for minimal-imports to work you must have done two things</strong></p>
<p><em>these are the caveats</em></p>
<ul>
<li>You have to have compiled your program with the GHC flag <code>-ddump-minimal-imports.</code> The more recently the better or you might not end up with the right imports.</li>
<li>You must have a single line comment <code>-- M.imports</code> selected above, and along, with your imports when you run the command. M.imports is named after your module. The file <code>src/Site/Slug.hs</code> would need <code>-- Site.Slug.imports</code> If either of those conditions is not met it won’t work. You’ll (well, should) merely end up with your imports unchanged. Maybe an extra new line. No big deal.</li>
</ul>
<h2 id="the-idea-the-inspiration-the-implementation">The idea, the inspiration, the implementation<a href="#the-idea-the-inspiration-the-implementation" class="anchor"> #</a></h2>
<p>While making this site, in fact, I was tired of doing all of this by hand. I asked around if there was a way to make it easier. <a href="https://gist.github.com/Kiwi/c3d02b3addc3d28649668aadef284aab">The original question.</a> Ed Kmett told me about -ddump-minimal-imports. As well as that there’s <a href="https://hackage.haskell.org/package/packunused">another tool</a> that makes use of that flag but it does not do the same thing. He couldn’t remember the name at the time but I later found out, obviously. Anyway, that was a start.</p>
<h3 id="the-hard-part-already-exists">The hard part already exists<a href="#the-hard-part-already-exists" class="anchor"> #</a></h3>
<p>If you’re not familiar with it beyond the mentioning above <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/separate_compilation.html#ghc-flag--ddump-minimal-imports">-ddump-minimal-imports</a> is a GHC flag that causes the compiler to create the files with the form M.imports. (where M is the module name) The contents of the file are the explicit imports that we desire in our final project.</p>
<h3 id="my-first-attempt">My first attempt<a href="#my-first-attempt" class="anchor"> #</a></h3>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co">#!/bin/sh</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="va">SCRIPT=$(</span><span class="ex">realpath</span> <span class="st">"</span><span class="va">$0</span><span class="st">"</span><span class="va">)</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a><span class="va">SCRIPTPATH=$(</span><span class="fu">dirname</span> <span class="st">"</span><span class="va">$SCRIPT</span><span class="st">"</span><span class="va">)</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a><span class="va">ROOTPATH=</span><span class="st">"</span><span class="va">$(</span><span class="bu">cd</span> <span class="st">"</span><span class="va">$SCRIPTPATH</span><span class="st">"</span>/../ <span class="kw">&&</span> <span class="bu">pwd</span><span class="va">)</span><span class="st">"</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">i</span> in <span class="st">"</span><span class="va">$ROOTPATH</span><span class="st">"</span>/dist-newstyle/build/x86_64-linux/ghc-8.6.4/*/*/*/build/site/site-tmp/*.imports<span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a> <span class="bu">printf</span> <span class="st">"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n%s\n\n"</span> <span class="st">"</span><span class="va">$i</span><span class="st">"</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a> <span class="fu">basename</span> <span class="st">"</span><span class="va">$i</span><span class="st">"</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a> <span class="bu">printf</span> <span class="st">"\n"</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a> <span class="fu">cat</span> <span class="st">"</span><span class="va">$i</span><span class="st">"</span><span class="kw">;</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a><span class="bu">printf</span> <span class="st">"\n\n\n"</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">i</span> in <span class="st">"</span><span class="va">$ROOTPATH</span><span class="st">"</span>/dist-newstyle/build/x86_64-linux/ghc-8.6.4/*/*/*/build/site/site-tmp/*.imports<span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true"></a> <span class="fu">basename</span> <span class="st">"</span><span class="va">$i</span><span class="st">"</span> <span class="kw">;</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true"></a></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true"></a></span></code></pre></div>
<p>(I don’t know shell if it’s bad that’s why) Obviously, while this makes finding and seeing the imports -ddump-minimal-imports easier, it still requires a lot of manual intervention and copy-paste. It so happens to be easily automated… as you’ve seen in this very post. I also didn’t notice there was a flag to set where they get dumped….</p>
<h3 id="second-attempt-editor-integration">Second attempt, editor integration!<a href="#second-attempt-editor-integration" class="anchor"> #</a></h3>
<p>There’s a <a href="http://www.haskellforall.com/2018/10/detailed-walkthrough-for-beginner.html">good blog post</a> by Gabriel Gonzalez that goes through and explains the implementation of a vim plugin that aligns equals signs. I won’t use it because I prefer running hindent on whole files to avoid <a href="http://catb.org/jargon/html/B/bikeshedding.html">bikeshedding</a> about formatting and decision paralysis. It’s one less thing I have to worry about if I let hindent decide the final format. I can focus on getting the code to work and let hindent indent it all proper like and consistent. Anyway, the idea was largely applicable (especially in maximal-imports, just look at it…) to the task at hand.</p>
<p>Anyway, combine all of that together and ta da; here we are.</p>
</section>
</div>
</div>
</div>
</summary>
</entry>
<entry>
<title>Pandoc Include Code</title>
<link href="https://mostlyabsurd.github.io/posts/2019/04/25/pandoc-include-code/" />
<id>https://mostlyabsurd.github.io/posts/2019/04/25/pandoc-include-code/</id>
<published>2019-04-25T00:00:00Z</published>
<updated>2019-09-15T00:00:00Z</updated>
<summary type="html"><div class="row">
<div class="col-md-8 offset-md-2 col-sm-10 offset-sm-1 p-md-1 p-4">
<section class="header">
<div class="col-md-6 offset-md-3 col-sm-8 offset-sm-2 p-1">
<h6><span>Posted on April 25, 2019 by Robert Djubek </span></h6>
</div>
<div class="col-md-6 offset-md-3 col-sm-8 offset-sm-2 p-1">
<h6><span class="blog-tag"> <a href="/tags/hakyll/">hakyll</a>, <a href="/tags/haskell/">haskell</a>, and <a href="/tags/pandoc/">pandoc</a> </span></h6>
</div>
</section>
<div>
<br />
<br />
<section>
<h2 id="how-to-include-code-from-files">How to include code from files<a href="#how-to-include-code-from-files" class="anchor"> #</a></h2>
<h3 id="pandoc-include-code"><a href="https://github.com/owickstrom/pandoc-include-code">Pandoc Include Code</a><a href="#pandoc-include-code" class="anchor"> #</a></h3>
<p>The Pandoc Include Code project lets you embed code in pandoc (and also <a href="https://jaspervdj.be/hakyll/">Hakyll</a>) projects.</p>
<p>Features include control over line numbers, language syntax highlighting, including files, and using snippets from portions of files.</p>
<div class="sourceCode" id="maincode" data-startFrom="20"><pre class="sourceCode numberSource haskell numberLines"><code class="sourceCode haskell" style="counter-reset: source-line 19;"><span id="maincode-20"><a href="#maincode-20"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="maincode-21"><a href="#maincode-21"></a>main <span class="ot">=</span> hakyll <span class="op">$</span> <span class="kw">do</span></span></code></pre></div>
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource haskell numberLines"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">import</span> <span class="dt">Text.Pandoc</span> (<span class="dt">Format</span> (..), <span class="dt">Pandoc</span>)</span>
<span id="cb1-2"><a href="#cb1-2"></a><span class="kw">import</span> <span class="dt">Text.Pandoc.Walk</span> (walkM)</span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="kw">import</span> <span class="dt">Text.Pandoc.Filter.IncludeCode</span> (includeCode)</span>
<span id="cb1-4"><a href="#cb1-4"></a></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="ot">includeCodeTransform ::</span> <span class="dt">Pandoc</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Pandoc</span></span>
<span id="cb1-6"><a href="#cb1-6"></a>includeCodeTransform <span class="ot">=</span> walkM (includeCode (<span class="dt">Just</span> (<span class="dt">Format</span> <span class="st">"html5"</span>)))</span>
<span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a><span class="ot">includeCodePandocCompiler ::</span> <span class="dt">Compiler</span> (<span class="dt">Item</span> <span class="dt">String</span>)</span>
<span id="cb1-9"><a href="#cb1-9"></a>includeCodePandocCompiler <span class="ot">=</span></span>
<span id="cb1-10"><a href="#cb1-10"></a> pandocCompilerWithTransformM</span>
<span id="cb1-11"><a href="#cb1-11"></a> defaultHakyllReaderOptions</span>
<span id="cb1-12"><a href="#cb1-12"></a> defaultHakyllWriterOptions</span>
<span id="cb1-13"><a href="#cb1-13"></a> (unsafeCompiler <span class="op">.</span> includeCodeTransform)</span></code></pre></div>
<div class="sourceCode" id="cb2" data-startFrom="20"><pre class="sourceCode numberSource haskell numberLines"><code class="sourceCode haskell" style="counter-reset: source-line 19;"><span id="cb2-20"><a href="#cb2-20"></a><span class="kw">import</span> <span class="dt">Text.Pandoc</span> (<span class="dt">Format</span> (..), <span class="dt">Pandoc</span>)</span>
<span id="cb2-21"><a href="#cb2-21"></a><span class="kw">import</span> <span class="dt">Text.Pandoc.Walk</span> (walkM)</span>
<span id="cb2-22"><a href="#cb2-22"></a><span class="kw">import</span> <span class="dt">Text.Pandoc.Filter.IncludeCode</span> (includeCode)</span>
<span id="cb2-23"><a href="#cb2-23"></a></span>
<span id="cb2-24"><a href="#cb2-24"></a><span class="ot">includeCodeTransform ::</span> <span class="dt">Pandoc</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Pandoc</span></span>
<span id="cb2-25"><a href="#cb2-25"></a>includeCodeTransform <span class="ot">=</span> walkM (includeCode (<span class="dt">Just</span> (<span class="dt">Format</span> <span class="st">"html5"</span>)))</span>
<span id="cb2-26"><a href="#cb2-26"></a></span>
<span id="cb2-27"><a href="#cb2-27"></a><span class="ot">includeCodePandocCompiler ::</span> <span class="dt">Compiler</span> (<span class="dt">Item</span> <span class="dt">String</span>)</span>
<span id="cb2-28"><a href="#cb2-28"></a>includeCodePandocCompiler <span class="ot">=</span></span>
<span id="cb2-29"><a href="#cb2-29"></a> pandocCompilerWithTransformM</span>
<span id="cb2-30"><a href="#cb2-30"></a> defaultHakyllReaderOptions</span>
<span id="cb2-31"><a href="#cb2-31"></a> defaultHakyllWriterOptions</span>
<span id="cb2-32"><a href="#cb2-32"></a> (unsafeCompiler <span class="op">.</span> includeCodeTransform)</span></code></pre></div>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Text.Pandoc</span> (<span class="dt">Format</span> (..), <span class="dt">Pandoc</span>)</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Text.Pandoc.Walk</span> (walkM)</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a><span class="kw">import</span> <span class="dt">Text.Pandoc.Filter.IncludeCode</span> (includeCode)</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a><span class="ot">includeCodeTransform ::</span> <span class="dt">Pandoc</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Pandoc</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>includeCodeTransform <span class="ot">=</span> walkM (includeCode (<span class="dt">Just</span> (<span class="dt">Format</span> <span class="st">"html5"</span>)))</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a><span class="ot">includeCodePandocCompiler ::</span> <span class="dt">Compiler</span> (<span class="dt">Item</span> <span class="dt">String</span>)</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a>includeCodePandocCompiler <span class="ot">=</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a> pandocCompilerWithTransformM</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a> defaultHakyllReaderOptions</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a> defaultHakyllWriterOptions</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a> (unsafeCompiler <span class="op">.</span> includeCodeTransform)</span></code></pre></div>
</section>
</div>
</div>
</div>
</summary>
</entry>
</feed>