<p><spanclass="sc"><abbrtitle="Too long; didn't read">tl;dr</abbr>: </span> Learn how to start a new Haskell project. Translate a starter tool written in <code>zsh</code> in Haskell using its own result.</p>
<blockquote>
<p>“Good Sir Knight, will you come with me to Camelot, and join us at the Round Table?”</p>
</blockquote>
<p>In order to work properly with Haskell you need to initialize your environment. Typically, you need to use a cabal file, create some test for your code. Both, unit test and propositional testing (random and exhaustive up to a certain depth). You need to use <code>git</code> and generally hosting it on github. Also, it is recommended to use cabal sandboxes. And as bonus, an auto-update tool that recompile and retest on each file save.</p>
<p>In this article, we will create such an environment using a zsh script. Then we will write a Haskell project which does the same work as the zsh script. You will then see how to work in such an environment.</p>
<p>If you are starting to understand Haskell but consider yourself a beginner, this tutorial will show you how to make a real application using quite surprisingly a lot of features:</p>
<ul>
<li>use colorized output</li>
<li>interact with a user in command line</li>
<li>read/write files</li>
<li>kind of parse a file (in fact, simply split it)</li>
<li>use a templating system (mustache: fill a data structure, write files)</li>
<li>make a HTTP GET request then parse the JSON answer and use it</li>
<li>use random</li>
<li>create a cabal package</li>
<li>add and use non source files to a cabal package</li>
<li>Test your code (both unit testing and property testing)</li>
</ul>
<p><strong>☞</strong> zsh is by its nature more suitable to file manipulation. But the Haskell code is clearly more organized while quite terse for a multi-purpose language.</p>
<p><strong>☞</strong> holy-project is on hackage. It can be installed with <code>cabal update && cabal install holy-project</code>.</p>
</div>
<p>I recently read this excellent article: <ahref="http://jabberwocky.eu/2013/10/24/how-to-start-a-new-haskell-project/">How to Start a New Haskell Project</a>.</p>
<p>While the article is very good, I lacked some minor informations<ahref="#fn1"class="footnote-ref"id="fnref1"><sup>1</sup></a>. Inspired by it, I created a simple script to initialize a new Haskell project. During the process I improved some things a bit.</p>
<p>If you want to use this script, the steps are:</p>
<p>Developing the script in <code>zsh</code> was easy. But considering its size, it is worth to rewrite it in Haskell. Furthermore, it will be a good exercise.</p>
<h3id="patricide">Patricide</h3>
<p>In a first time, we initialize a new Haskell project with <code>holy-haskell.sh</code>:</p>
<pre>
> ./holy-haskell.sh
<spanclass="green">Bridgekeeper: Stop!
Bridgekeeper: Who would cross the Bridge of Death
Bridgekeeper: must answer me these questions three,
Bridgekeeper: ere the other side he see.</span>
<spanclass="yellow">You: Ask me the questions, bridgekeeper, I am not afraid.</span>
<spanclass="green">Bridgekeeper: What is the name of your project?</span>
> Holy project
<spanclass="green">Bridgekeeper: What is your name?</span> (Yann Esposito (Yogsototh))
>
<spanclass="green">Bridgekeeper: What is your email?</span> (Yann.Esposito@gmail.com)
>
<spanclass="green">Bridgekeeper: What is your github user name?</span> (yogsototh)
>
<spanclass="green">Bridgekeeper: What is your project in less than ten words?</span>
> Start your Haskell project with cabal, git and tests.
Initialize git
Initialized empty Git repository in .../holy-project/.git/
Create files
.gitignore
holy-project.cabal
Setup.hs
LICENSE (MIT)
test/Test.hs
test/HolyProject/Swallow/Test.hs
src/HolyProject/Swallow.hs
test/HolyProject/Coconut/Test.hs
src/HolyProject/Coconut.hs
src/HolyProject.hs
src/Main.hs
Cabal sandboxing, install and test
...
many compilations lines
...
Running 1 test suites...
Test suite Tests: RUNNING...
Test suite Tests: PASS
Test suite logged to: dist/test/holy-project-0.1.0.0-Tests.log
1 of 1 test suites (1 of 1 test cases) passed.
All Tests
Swallow
swallow test: <spanclass="green">OK</span>
coconut
coconut: <spanclass="green">OK</span>
coconut property: <spanclass="green">OK</span>
148 tests completed
<spanclass="green">All 3 tests passed</span>
<spanclass="green">Bridgekeeper: What... is the air-speed velocity of an unladen swallow?</span>
<spanclass="yellow">You: What do you mean? An African or European swallow?</span>
<spanclass="green">Bridgekeeper: Huh? I... I don't know that.</span>
Sir Bedevere: How do you know so much about swallows?
<spanclass="yellow">You: Well, you have to know these things when you're a king, you know.</span>
</pre>
<p>The different steps are:</p>
<ul>
<li>small introduction quotes</li>
<li>ask five questions –<em>three question sir…</em></li>
<li>create the directory for the project</li>
<li>init git</li>
<li>create files</li>
<li>sandbox cabal</li>
<li>cabal install and test</li>
<li>run the test directly in the terminal</li>
<li>small goodbye quotes</li>
</ul>
<p>Features to note:</p>
<ul>
<li>color in the terminal</li>
<li>check some rules on the project name</li>
<li>random message if error</li>
<li>use <code>~/.gitconfig</code> file in order to provide a default name and email.</li>
<li>use the github API which returns JSON to get the default github user name.</li>
</ul>
<p>So, apparently nothing too difficult to achieve.</p>
<p>We should now have an initialized Haskell environment for us to work. The first thing you should do, is to go into this new directory and launch ‘./auto-update’ in some terminal. I personally use <code>tmux</code> on Linux or the splits in <code>iTerm 2</code> on Mac OS X. Now, any modification of a source file will relaunch a compilation and a test.</p>
<h3id="the-dialogs">The dialogs</h3>
<div>
<imgsrc="../../../../Scratch/img/blog/Holy-Haskell-Starter/bridge-of-death.jpg"alt="Bridge of Death"/>
</div>
<p>To print the introduction text in <code>zsh</code>:</p>
<aclass="sourceLine"id="cb3-4"title="4"><spanclass="kw">for</span><spanclass="ex">COLOR</span> in RED GREEN YELLOW BLUE MAGENTA CYAN BLACK WHITE<spanclass="kw">;</span><spanclass="kw">do</span></a>
<aclass="sourceLine"id="cb3-16"title="16"><spanclass="ex">bk</span><spanclass="st">"Who would cross the Bridge of Death"</span></a>
<aclass="sourceLine"id="cb3-17"title="17"><spanclass="ex">bk</span><spanclass="st">"must answer me these questions three,"</span></a>
<aclass="sourceLine"id="cb3-18"title="18"><spanclass="ex">bk</span><spanclass="st">"ere the other side he see."</span></a>
<aclass="sourceLine"id="cb3-19"title="19"><spanclass="ex">you</span><spanclass="st">"Ask me the questions, bridgekeeper, I am not afraid.\n"</span></a>
<aclass="sourceLine"id="cb3-23"title="23"><spanclass="ex">bk</span><spanclass="st">"What... is the air-speed velocity of an unladen swallow?"</span></a>
<aclass="sourceLine"id="cb3-24"title="24"><spanclass="ex">you</span><spanclass="st">"What do you mean? An African or European swallow?"</span></a>
<aclass="sourceLine"id="cb3-25"title="25"><spanclass="ex">bk</span><spanclass="st">"Huh? I... I don't know that."</span></a>
<aclass="sourceLine"id="cb3-26"title="26"><spanclass="ex">log</span><spanclass="st">"[the bridgekeeper is thrown over]"</span></a>
<aclass="sourceLine"id="cb3-28"title="28"><spanclass="ex">log</span><spanclass="st">"Sir Bedevere: How do you know so much about swallows?"</span></a>
<aclass="sourceLine"id="cb3-29"title="29"><spanclass="ex">you</span><spanclass="st">"Well, you have to know these things when you're a king, you know."</span></a></code></pre></div>
<p>In the first Haskell version I don’t use colors. We see we can almost copy/paste. I just added the types.</p>
<aclass="sourceLine"id="cb4-21"title="21"> bk <spanclass="st">"What... is the air-speed velocity of an unladen swallow?"</span></a>
<aclass="sourceLine"id="cb4-22"title="22"> you <spanclass="st">"What do you mean? An African or European swallow?"</span></a>
<aclass="sourceLine"id="cb4-23"title="23"> bk <spanclass="st">"Huh? I... I don't know that."</span></a>
<aclass="sourceLine"id="cb4-24"title="24"><spanclass="fu">putStrLn</span><spanclass="st">"[the bridgekeeper is thrown over]"</span></a>
<aclass="sourceLine"id="cb4-25"title="25"> bk <spanclass="st">"Auuuuuuuuuuuugh"</span></a>
<aclass="sourceLine"id="cb4-26"title="26"><spanclass="fu">putStrLn</span><spanclass="st">"Sir Bedevere: How do you know so much about swallows?"</span></a>
<aclass="sourceLine"id="cb4-27"title="27"> you <spanclass="st">"Well, you have to know these things when you're a king, you know."</span></a></code></pre></div>
<p>Now let’s just add the colors using the <ahref="http://hackage.haskell.org/package/ansi-terminal"><code>ansi-terminal</code></a> package. So we have to add <code>ansi-terminal</code> as a build dependency in our cabal file.</p>
<p>Edit <code>holy-project.cabal</code> to add it.</p>
<aclass="sourceLine"id="cb6-30"title="30"> bk <spanclass="st">"What... is the air-speed velocity of an unladen swallow?"</span></a>
<aclass="sourceLine"id="cb6-31"title="31"> you <spanclass="st">"What do you mean? An African or European swallow?"</span></a>
<aclass="sourceLine"id="cb6-32"title="32"> bk <spanclass="st">"Huh? I... I don't know that."</span></a>
<aclass="sourceLine"id="cb6-33"title="33"><spanclass="fu">putStrLn</span><spanclass="st">"[the bridgekeeper is thrown over]"</span></a>
<aclass="sourceLine"id="cb6-34"title="34"> bk <spanclass="st">"Auuuuuuuuuuuugh"</span></a>
<aclass="sourceLine"id="cb6-35"title="35"><spanclass="fu">putStrLn</span><spanclass="st">"Sir Bedevere: How do you know so much about swallows?"</span></a>
<aclass="sourceLine"id="cb6-36"title="36"> you <spanclass="st">"Well, you have to know these things when you're a king, you know."</span></a></code></pre></div>
<p>We could put this code in <code>src/Main.hs</code>. Declare a main function:</p>
<p>Make <code>cabal install</code> and run <code>cabal run</code> (or <code>./.cabal-sandbox/bin/holy-project</code>). It works!</p>
<h2id="five-questions-three-questions-sir">Five Questions – Three questions Sir!</h2>
<div>
<imgsrc="../../../../Scratch/img/blog/Holy-Haskell-Starter/bring-out-your-dead.jpg"alt="Bring out your dead!"/>
</div>
<p>In order to ask questions, here is how we do it in shell script:</p>
<divclass="sourceCode"id="cb8"><preclass="sourceCode bash"><codeclass="sourceCode bash"><aclass="sourceLine"id="cb8-1"title="1"><spanclass="ex">print</span> -- <spanclass="st">"What is your name?"</span></a>
<p>If we want to abstract things a bit, the easiest way in shell is to use a global variable<ahref="#fn2"class="footnote-ref"id="fnref2"><sup>2</sup></a> which will get the value of the user input like this:</p>
<aclass="sourceLine"id="cb9-4"title="4"><spanclass="ex">bk</span><spanclass="st">"What is your </span><spanclass="va">$info</span><spanclass="st">?"</span></a>
<aclass="sourceLine"id="cb10-4"title="4">ask info <spanclass="ot">=</span><spanclass="kw">do</span></a>
<aclass="sourceLine"id="cb10-5"title="5"> bk <spanclass="op">$</span><spanclass="st">"What is your "</span><spanclass="op">++</span> info <spanclass="op">++</span><spanclass="st">"?"</span></a>
<p>You could test it with <code>cabal install</code> and then <code>./.cabal-sandbox/bin/holy-project</code>.</p>
<p>We will see later how to guess the answer using the <code>.gitconfig</code> file and the github API.</p>
<h2id="using-answers">Using answers</h2>
<div>
<imgsrc="../../../../Scratch/img/blog/Holy-Haskell-Starter/castle-of-hhhhaaaarr.jpg"alt="Castle of Aaaaarrrr????"/>
</div>
<h3id="create-the-project-name">Create the project name</h3>
<p>I don’t really like the ability to use capital letter in a package name. So in shell I transform the project name like this:</p>
<divclass="sourceCode"id="cb12"><preclass="sourceCode bash"><codeclass="sourceCode bash"><aclass="sourceLine"id="cb12-1"title="1"><spanclass="co"># replace all spaces by dashes then lowercase the string</span></a>
<divclass="sourceCode"id="cb16"><preclass="sourceCode haskell"><codeclass="sourceCode haskell"><aclass="sourceLine"id="cb16-1"title="1"><spanclass="co">-- | transform a chain like "Holy project" in "HolyProject"</span></a>
<p>The haskell version is made by hand where zsh already had a capitalize operation on string with many words. Here is the difference between the shell and haskell way (note I splitted the effect of <code>concatMap</code> as <code>map</code> and <code>concat</code>):</p>
<pre><code>shell:
"Holy-grail" ==( sed 's/-/ /g' )=>"Holy<spanclass="highlight"></span>grail"
<p>Which verify the project name is not empty and use only letter, numbers and dashes:</p>
<divclass="sourceCode"id="cb19"><preclass="sourceCode haskell"><codeclass="sourceCode haskell"><aclass="sourceLine"id="cb19-1"title="1"><spanclass="co">-- | verify if project name is conform</span></a>
<p>In Haskell, while possible, we shouldn’t put the file content in the source code. We have a relatively easy way to include external file in a cabal package. This is what we will be using.</p>
<p>Furthermore, we need a templating system to replace small part of the static file by computed values. For this task, I choose to use <ahref="http://hackage.haskell.org/package/hastache"><code>hastache</code></a>, a Haskell implementation of Mustache templates<ahref="#fn3"class="footnote-ref"id="fnref3"><sup>3</sup></a>.</p>
<h3id="add-external-files-in-a-cabal-project">Add external files in a cabal project</h3>
<p>Cabal provides a way to add files which are not source files to a package. You simply have to add a <code>Data-Files:</code> entry in the header of the cabal file:</p>
<pre><code>data-files: scaffold/LICENSE
, scaffold/Setup.hs
, scaffold/auto-update
, scaffold/gitignore
, scaffold/interact
, scaffold/project.cabal
, scaffold/src/Main.hs
, scaffold/src/ModuleName.hs
, scaffold/src/ModuleName/Coconut.hs
, scaffold/src/ModuleName/Swallow.hs
, scaffold/test/ModuleName/Coconut/Test.hs
, scaffold/test/ModuleName/Swallow/Test.hs
, scaffold/test/Test.hs</code></pre>
<p>Now we simply have to create our files at the specified path. Here is for example the first lines of the LICENSE file.</p>
Permission is hereby granted, free of charge, to any person obtaining a copy
...</code></pre>
<p>It will be up to our program to replace the <code>{{year}}</code> and <code>{{author}}</code> at runtime. We have to find the files. Cabal will create a module named <code>Paths_holy_project</code>. If we import this module we have the function <code>genDataFileName</code> at our disposal. Now we can read the files at runtime like this:</p>
<h3id="create-files-and-directories">Create files and directories</h3>
<p>A first remark is for portability purpose we shouldn’t use String for file path. For example on Windows <code>/</code> isn’t considered as a subdirectory character. To resolve this problem we will use <code>FilePath</code>:</p>
<p>In order to use hastache we can either create a context manually or use generics to create a context from a record. This is the last option we will show here. So in a first time, we need to import some modules and declare a record containing all necessary informations to create our project.</p>
<divclass="sourceCode"id="cb25"><preclass="sourceCode haskell"><codeclass="sourceCode haskell"><aclass="sourceLine"id="cb25-1"title="1"><spanclass="ot">{-# LANGUAGE DeriveDataTypeable #-}</span></a>
<aclass="sourceLine"id="cb27-12"title="12"><spanclass="highlight">in_synopsis</span><spanclass="ot"><-</span> ask <spanclass="st">"project in less than a dozen word?"</span></a>
<p>We use external files in mustache format. We ask question to our user to fill a data structure. We use this data structure to create a context. Hastache use this context with the external files to create the project files.</p>
<aclass="sourceLine"id="cb30-9"title="9"> bk <spanclass="st">"What... is your favourite colour?"</span></a>
<aclass="sourceLine"id="cb30-10"title="10"> you <spanclass="st">"Blue. No, yel..."</span></a>
<aclass="sourceLine"id="cb30-11"title="11"><spanclass="fu">putStrLn</span><spanclass="st">"[You are thrown over the edge into the volcano]"</span></a>
<aclass="sourceLine"id="cb30-12"title="12"> you <spanclass="st">"You: Auuuuuuuuuuuugh"</span></a>
<aclass="sourceLine"id="cb30-13"title="13"> bk <spanclass="st">" Hee hee heh."</span></a>
<aclass="sourceLine"id="cb30-16"title="16"> bk <spanclass="st">"What is the capital of Assyria?"</span></a>
<aclass="sourceLine"id="cb30-17"title="17"> you <spanclass="st">"I don't know that!"</span></a>
<aclass="sourceLine"id="cb30-18"title="18"><spanclass="fu">putStrLn</span><spanclass="st">"[You are thrown over the edge into the volcano]"</span></a>
<aclass="sourceLine"id="cb30-19"title="19"> you <spanclass="st">"Auuuuuuuuuuuugh"</span></a>
<p>We want to retrieve the <code>~/.gitconfig</code> file content and see if it contains a name and email information. We will need to access to the <code>HOME</code> environment variable. Also, as we use bytestring package for hastache, let’s take advantage of this library.</p>
<aclass="sourceLine"id="cb33-2"title="2">ask info hint <spanclass="ot">=</span><spanclass="kw">do</span></a>
<aclass="sourceLine"id="cb33-3"title="3"> bk <spanclass="op">$</span><spanclass="st">"What is your "</span><spanclass="op">++</span> info <spanclass="op">++</span><spanclass="st">"?"</span><spanclass="op">++</span><spanclass="highlight">(<spanclass="fu">maybe</span><spanclass="st">""</span> (\h <spanclass="ot">-></span><spanclass="st">" ("</span><spanclass="op">++</span>h<spanclass="op">++</span><spanclass="st">")"</span>) hint)</span></a>
<aclass="sourceLine"id="cb34-14"title="14"><spanclass="co">-- return the first Just value of a list of Maybe</span></a>
<aclass="sourceLine"id="cb34-15"title="15"><spanclass="ot">firstJust ::</span> (<spanclass="dt">Eq</span> a) <spanclass="ot">=></span> [<spanclass="dt">Maybe</span> a] <spanclass="ot">-></span><spanclass="dt">Maybe</span> a</a>
<aclass="sourceLine"id="cb34-16"title="16">firstJust l <spanclass="ot">=</span><spanclass="kw">case</span><spanclass="fu">dropWhile</span> (<spanclass="op">==</span><spanclass="dt">Nothing</span>) l <spanclass="kw">of</span></a>
<aclass="sourceLine"id="cb34-24"title="24"><spanclass="ot">-></span> [<spanclass="dt">LZ.ByteString</span>] <spanclass="co">-- line of words</span></a>
<aclass="sourceLine"id="cb34-25"title="25"><spanclass="ot">-></span><spanclass="dt">Maybe</span><spanclass="dt">String</span><spanclass="co">-- the value if found</span></a>
<aclass="sourceLine"id="cb34-26"title="26">getValueForKey el (n<spanclass="op">:</span>e<spanclass="op">:</span>xs) <spanclass="ot">=</span><spanclass="kw">if</span> (n <spanclass="op">==</span> (LZ.pack el)) <spanclass="op">&&</span> (e <spanclass="op">==</span> (LZ.pack <spanclass="st">"="</span>))</a>
<p>We could notice, <code>getNameAndMail</code> doesn’t read the full file and stop at the first occurrence of name and mail.</p>
<h3id="use-the-github-api">Use the github API</h3>
<div>
<imgsrc="../../../../Scratch/img/blog/Holy-Haskell-Starter/coconut.jpg"alt="Coconut and Swallow"/>
</div>
<p>The task seems relatively easy, but we’ll see there will be some complexity hidden. Make a request on <code>https://api.github.com/search/users?q=<email></code>. Parse the JSON and get the <code>login</code> field of the first item.</p>
<p>So the first problem to handle is to connect an URL. For this we will use the <code>http-conduit</code> package.</p>
<p>Generally, for simple request, we should use:</p>
<p>But, after some research, I discovered we must declare an User-Agent in the HTTP header to be accepted by the github API. So we have to change the HTTP Header, and our code became slightly more complex:</p>
<divclass="sourceCode"id="cb36"><preclass="sourceCode haskell"><codeclass="sourceCode haskell"><aclass="sourceLine"id="cb36-1"title="1"><spanclass="ot">{-# LANGUAGE OverloadedStrings #-}</span></a>
<p>So now, we have a String containing a JSON representation. In javascript we would have used <code>login=JSON.parse(body).items[0].login</code>. How does Haskell will handle it (knowing the J in JSON is for Javascript)?</p>
<p>First we will need to add the <code>lens-aeson</code> package and use it that way:</p>
<p>It looks ugly, but it’s terse. In fact each function <code>(^?)</code>, <code>key</code> and <code>nth</code> has some great mathematical properties and everything is type safe. Unfortunately I had to make my own <code>jsonValueToString</code>. I hope I simply missed a simpler existing function.</p>
<p>You can read <ahref="https://www.fpcomplete.com/user/tel/lens-aeson-traversals-prisms">this article on <code>lens-aeson</code> and prisms</a> to know more.</p>
<p>We now have all the feature provided by the original <code>zsh</code> script shell. But here is a good occasion to use some Haskell great feature.</p>
<p>We will launch the API request sooner and in parallel to minimize our wait time:</p>
<p>While it might feel a bit confusing, it is in fact quite simple.</p>
<oltype="1">
<li>declare an <ahref="http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent-MVar.html"><code>MVar</code></a>. Mainly a variable which either is empty or contains something.</li>
<li>If we didn’t found any email hint, put Nothing in the <code>MVar</code>.</li>
<li>If we have an email hint, ask on the github API in a new process and once finished put the result in the <code>MVar</code>.</li>
<li>If the user enter an email different from the hint email, then just request the github api now.</li>
<li>If the user enter the same email, then wait for the MVar to be filled and ask the next question with the result.</li>
</ol>
<p>If you have a github account and had set correctly your <code>.gitconfig</code>, you might not even wait.</p>
<h2id="project-structure">Project Structure</h2>
<p>We have a working product. But, I don’t consider our job finished. The code is about 335 lines.</p>
<p>Considering that we:</p>
<ul>
<li>have 29 lines of import and 52 lines of comments (rest 255 lines)</li>
<li>ask questions</li>
<li>use a templating system to generate files</li>
<li>call an asynchronous HTTP request</li>
<li>parse JSON</li>
<li>parse <code>.gitconfig</code></li>
<li>use colored output</li>
</ul>
<p>This is quite few.</p>
<h3id="modularizing">Modularizing</h3>
<div>
<imgsrc="../../../../Scratch/img/blog/Holy-Haskell-Starter/black-knight.jpg"alt="The Black Knight"/>
</div>
<p>For short programs it is not obvious to split them into different modules. But my personal preference is to split it anyway.</p>
<p>First we put all content of <code>src/Main.hs</code> in <code>src/HolyProject.hs</code>. We rename the <code>main</code> function by <code>holyStarter</code>. And our <code>src/Main.hs</code> should contains:</p>
<p>The <code>HolyProject.hs</code> file contains mostly the code that ask questions, show errors and copy files using hastache.</p>
<p>One of the benefits in modularizing the code is that our main code is clearer. Some functions are declared only in a module and are not exported. This help us hide technical details. For example, the modification of the HTTP header to use the github API.</p>
<p>We didn’t take much advantage of the project structure yet. A first thing is to generate some documentation. Before most function I added comment starting with <code>-- |</code>. These comment will be used by haddock to create a documentation. First, you need to install <code>haddock</code> manually.</p>
<p>Be sure to have <code>haddock</code> in your <code>PATH</code>. You could for example add it like this:</p>
<divclass="sourceCode"id="cb41"><preclass="sourceCode bash"><codeclass="sourceCode bash"><aclass="sourceLine"id="cb41-1"title="1"><spanclass="co"># You might want to add this line in your .profile</span></a>
<p>And magically, you’ll have a documentation in <code>dist/doc/html/holy-project/index.html</code>.</p>
<h3id="tests">Tests</h3>
<p>While the Haskell static typing is quite efficient to prevent entire classes of bugs, Haskell doesn’t discard the need to test to minimize the number of bugs.</p>
<h4id="unit-testing-with-hunit">Unit Testing with HUnit</h4>
<div>
<imgsrc="../../../../Scratch/img/blog/Holy-Haskell-Starter/witch.jpg"alt="A Witch! A Witch!"/>
</div>
<p>It is generally said to test we should use unit testing for code in IO and QuickCheck or SmallCheck for pure code.</p>
<p>A unit test example on pure code is in the file <code>test/HolyProject/Swallow/Test.hs</code>:</p>
<p>Note <code>swallow</code> is <code>(++)</code>. We group tests by group. Each group can contain some test suite. Here we have a test suite with only one test. The <code>(@=?)</code> verify the equality between its two parameters.</p>
<p>So now, we could safely delete the directory <code>test/HolyProject/Swallow</code> and the file <code>src/HolyProject/Swallow.hs</code>. And we are ready to make our own real world unit test. We will first test the module <code>HolyProject.GithubAPI</code>. Let’s create a file <code>test/HolyProject/GithubAPI/Test.hs</code> with the following content:</p>
<aclass="sourceLine"id="cb44-18"title="18"><spanclass="co">-- | Test if some IO action returns some expected value</span></a>
<aclass="sourceLine"id="cb44-19"title="19"><spanclass="ot">ioTestEq ::</span> (<spanclass="dt">Eq</span> a, <spanclass="dt">Show</span> a) <spanclass="ot">=></span><spanclass="dt">IO</span> a <spanclass="ot">-></span> a <spanclass="ot">-></span><spanclass="dt">Assertion</span></a>
<p>You have to modify your cabal file. More precisely, you have to add <code>HolyProject.GithubAPI</code> in the exposed modules of the library secion). You also have to update the <code>test/Test.hs</code> file to use <code>GithubAPI</code> instead of <code>Swallow</code>.</p>
<p>So we have our example of unit testing using IO. We search the github nickname for some people I know and we verify github continue to give the same answer as expected.</p>
<h4id="property-testing-with-smallcheck-and-quickcheck">Property Testing with SmallCheck and QuickCheck</h4>
<div>
<imgsrc="../../../../Scratch/img/blog/Holy-Haskell-Starter/zoot.jpg"alt="My name is Zoot. Just Zoot"/>
</div>
<p>When it comes to pure code, a very good method is to use QuickCheck and SmallCheck. SmallCheck will verify all cases up to some depth about some property. While QuickCheck will verify some random cases.</p>
<p>As this kind of verification of property is mostly doable on pure code, we will test the <code>StringUtils</code> module.</p>
<p>So don’t forget to declare <code>HolyProject.StringUtils</code> in the exposed modules in the library section of your cabal file. Remove all references to the <code>Coconut</code> module.</p>
<p>Modify the <code>test/Test.hs</code> to remove all references about <code>Coconut</code>. Create a <code>test/HolyProject/StringUtils/Test.hs</code> file containing:</p>
<aclass="sourceLine"id="cb45-21"title="21">idempotent f <spanclass="ot">=</span> \s <spanclass="ot">-></span> f s <spanclass="op">==</span> f (f s)</a>
<aclass="sourceLine"id="cb45-22"title="22"></a>
<aclass="sourceLine"id="cb45-23"title="23"><spanclass="ot">deeperIdempotent ::</span> (<spanclass="dt">Eq</span> a, <spanclass="dt">Show</span> a, <spanclass="dt">Serial</span> m a) <spanclass="ot">=></span> (a <spanclass="ot">-></span> a) <spanclass="ot">-></span><spanclass="dt">SC.Property</span> m</a>
<aclass="sourceLine"id="cb45-24"title="24">deeperIdempotent f <spanclass="ot">=</span> forAll <spanclass="op">$</span> SC.changeDepth1 (<spanclass="op">+</span><spanclass="dv">1</span>) <spanclass="op">$</span> \s <spanclass="ot">-></span> f s <spanclass="op">==</span> f (f s)</a></code></pre></div>
*** Failed! Falsifiable (after 19 tests and 5 shrinks):
"a a"
Use --quickcheck-replay '18 913813783 2147483380' to reproduce.
GithubAPI
Yann: <spanclass="green">OK</span>
Jasper: <spanclass="green">OK</span>
<spanclass="red">1 out of 5 tests failed</span>
</pre>
<p>The test fail, but this is not an error. Our <code>capitalize</code> function shouldn’t be idempotent. I simply added this test to show what occurs when a test fail. If you want to look more closely to the error you could do this:</p>
<p>It is important to use <code>./interact</code> instead of <code>ghci</code>. Because we need to tell <code>ghci</code> how to found the package installed.</p>
<p>Apparently, SmallCheck didn’t found any counter example. I don’t know how it generates Strings and using deeper search is really long.</p>
<p>Now you could start programming in Haskell and publish your own cabal package.</p>
<sectionclass="footnotes">
<hr/>
<ol>
<liid="fn1"><p>For example, you have to install the test libraries manually to use <code>cabal test</code>.<ahref="#fnref1"class="footnote-back">↩</a></p></li>
<liid="fn2"><p>There is no easy way to do something like <code>name=$(ask name)</code>. Simply because <code>$(ask name)</code> run in another process which doesn’t get access to the standard input<ahref="#fnref2"class="footnote-back">↩</a></p></li>
<liid="fn3"><p>Having a good level of power in templates is very difficult. <spanclass="sc"><abbrtitle="In my Humble Opinion">imho</abbr></span> Mustache has made the best compromise.<ahref="#fnref3"class="footnote-back">↩</a></p></li>