her.esy.fun/src/Scratch/en/blog/Haskell-Tutorials--a-tutorial/index.html

366 lines
24 KiB
HTML
Raw Normal View History

2021-04-18 10:23:24 +00:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>YBlog - Haskell Tutorials, a tutorial</title>
<meta name="keywords" content="programming, tutorial, haskell, documentation" />
<link rel="shortcut icon" type="image/x-icon" href="../../../../Scratch/img/favicon.ico" />
<link rel="stylesheet" type="text/css" href="../../../../Scratch/css/brutalist.css" />
<link rel="stylesheet" type="text/css" href="../../../../Scratch/css/pandoc-solarized.css" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="../../../../Scratch/en/blog/feed/feed.xml" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" href="../../../../Scratch/img/about/FlatAvatar@2x.png" />
<!--[if lt IE 9]>
<script src="http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
<![endif]-->
<!-- IndieAuth -->
<link href="https://twitter.com/yogsototh" rel="me">
<link href="https://github.com/yogsototh" rel="me">
<link href="mailto:yann.esposito@gmail.com" rel="me">
<link rel="pgpkey" href="../../../../pubkey.txt">
</head>
<body lang="en" class="article">
<div id="content">
<div id="header">
<div id="choix">
<span id="choixlang">
<a href="../../../../Scratch/fr/blog/Haskell-Tutorials--a-tutorial/">French</a>
</span>
<span class="tomenu"><a href="#navigation">↓ Menu ↓</a></span>
<span class="flush"></span>
</div>
</div>
<div id="titre">
<h1>Haskell Tutorials, a tutorial</h1>
</div>
<div class="flush"></div>
<div id="afterheader" class="article">
<div class="corps">
<div>
<img src="../../../../Scratch/img/blog/Haskell-Tutorials--a-tutorial/main.jpg" alt="Main image" />
</div>
<div class="intro">
<p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> Some hints on how to make great documentation for Haskell libraries.</p>
<ol type="1">
<li>Create a <code>Tutorial</code> module containing nothing except documentation.</li>
<li>Mention the <code>Tutorial</code> module in your <code>cabal</code> description</li>
<li>Use <code>doctest</code> to check your documentation is up to date</li>
<li>For more complex real world examples, link to the source of some test.</li>
</ol>
</div>
<p>Great documentation make a big difference. A bad documentation could simply make people not using your lib.</p>
<p>My friend was learning Haskell. To start he tried a Haskell library to make a small application. The documentation was deprecated to the point he wasnt able to make a basic example work. How do you believe he felt? What does he thought about Haskell in general?</p>
<p>So here are my hint on how to make a great documentation in Haskell.</p>
<p>Documentation can take many different form.</p>
<ol type="1">
<li><strong>Tutorials/Guides</strong> write some prose which friendly take a user by hand and help him</li>
<li><strong>Examples</strong> how to use each function</li>
<li><strong>Generated API Documentation</strong> haddock</li>
</ol>
<h2 id="hints">Hints</h2>
<h3 id="tutorialsguides">Tutorials/Guides</h3>
<ol type="1">
<li>Create a new module named <code>Tutorial</code> (or <code>Guide.GuideTopic</code>)</li>
<li>Create a link to the tutorial in the cabal description</li>
<li>Create a link to the tutorial in your README</li>
<li>Here is an example some <code>Tutorial</code> module content:</li>
</ol>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb1-1" title="1"><span class="ot">{-# OPTIONS_GHC -fno-warn-unused-imports #-}</span></a>
<a class="sourceLine" id="cb1-2" title="2"><span class="co">{-|</span></a>
<a class="sourceLine" id="cb1-3" title="3"><span class="co"> Use @my-package@ if you want to ...</span></a>
<a class="sourceLine" id="cb1-4" title="4"><span class="co">-}</span></a>
<a class="sourceLine" id="cb1-5" title="5"><span class="kw">module</span> <span class="dt">Data.Duration.Tutorial</span> (</a>
<a class="sourceLine" id="cb1-6" title="6"></a>
<a class="sourceLine" id="cb1-7" title="7"> <span class="co">-- * Introduction</span></a>
<a class="sourceLine" id="cb1-8" title="8"> <span class="co">-- $introduction</span></a>
<a class="sourceLine" id="cb1-9" title="9"></a>
<a class="sourceLine" id="cb1-10" title="10"> <span class="co">-- ** Subsection</span></a>
<a class="sourceLine" id="cb1-11" title="11"> <span class="co">-- $subsection</span></a>
<a class="sourceLine" id="cb1-12" title="12"></a>
<a class="sourceLine" id="cb1-13" title="13"> ) <span class="kw">where</span></a>
<a class="sourceLine" id="cb1-14" title="14"></a>
<a class="sourceLine" id="cb1-15" title="15"><span class="kw">import</span> <span class="dt">Data.Duration</span></a>
<a class="sourceLine" id="cb1-16" title="16"></a>
<a class="sourceLine" id="cb1-17" title="17"><span class="co">{- $introduction</span></a>
<a class="sourceLine" id="cb1-18" title="18"></a>
<a class="sourceLine" id="cb1-19" title="19"><span class="co">So here how you use it:</span></a>
<a class="sourceLine" id="cb1-20" title="20"></a>
<a class="sourceLine" id="cb1-21" title="21"><span class="co"> &gt;&gt;&gt; humanReadableDuration 1002012.002</span></a>
<a class="sourceLine" id="cb1-22" title="22"><span class="co"> &quot;11 days 14 hours 20 min 12s 2ms&quot;</span></a>
<a class="sourceLine" id="cb1-23" title="23"></a>
<a class="sourceLine" id="cb1-24" title="24"><span class="co">The function is 'humanReadableDuration' and</span></a>
<a class="sourceLine" id="cb1-25" title="25"><span class="co">the you'll be able to click on it to go</span></a>
<a class="sourceLine" id="cb1-26" title="26"><span class="co">to its definition.</span></a>
<a class="sourceLine" id="cb1-27" title="27"></a>
<a class="sourceLine" id="cb1-28" title="28"><span class="co">You can add images: &lt;&lt;path-to-image.png title&gt;&gt;</span></a>
<a class="sourceLine" id="cb1-29" title="29"><span class="co">and links: &lt;http://haskell-lang.org haskell&gt;.</span></a>
<a class="sourceLine" id="cb1-30" title="30"><span class="co"> </span></a>
<a class="sourceLine" id="cb1-31" title="31"><span class="co">-}</span></a>
<a class="sourceLine" id="cb1-32" title="32"></a>
<a class="sourceLine" id="cb1-33" title="33"><span class="co">{- $subsection</span></a>
<a class="sourceLine" id="cb1-34" title="34"></a>
<a class="sourceLine" id="cb1-35" title="35"><span class="co">This is a chuck of documentation</span></a>
<a class="sourceLine" id="cb1-36" title="36"><span class="co">not attached to any particular Haskell</span></a>
<a class="sourceLine" id="cb1-37" title="37"><span class="co">declaration with an untested code block:</span></a>
<a class="sourceLine" id="cb1-38" title="38"></a>
<a class="sourceLine" id="cb1-39" title="39"><span class="co">&gt; answer = 42</span></a>
<a class="sourceLine" id="cb1-40" title="40"></a>
<a class="sourceLine" id="cb1-41" title="41"><span class="co">-}</span></a></code></pre></div>
<p>To prevent obsolescence of your tutorial, use <code>doctest</code>.</p>
<p>That way when youll do a <code>stack test</code> or <code>cabal test</code> youll get errors if some example doesnt work anymore.</p>
<h3 id="examples-doctest">Examples (doctest)</h3>
<p><code>doctest</code> is a great way to provide examples in your code documentation. These example will then be used as tests. Apparently it comes from Python community.</p>
<p>To use <code>doctest</code>, this is very simple:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb2-1" title="1"><span class="co">-- | My function description</span></a>
<a class="sourceLine" id="cb2-2" title="2"><span class="co">-- </span></a>
<a class="sourceLine" id="cb2-3" title="3"><span class="co">-- &gt;&gt;&gt; myFunction 3 4</span></a>
<a class="sourceLine" id="cb2-4" title="4"><span class="co">-- 7</span></a>
<a class="sourceLine" id="cb2-5" title="5"><span class="ot">myFunction ::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span></a>
<a class="sourceLine" id="cb2-6" title="6">myFunction <span class="ot">=</span> (<span class="op">+</span>)</a></code></pre></div>
<p>And to make it works simply verify you have a <code>test</code> bloc in your <code>.cabal</code> file looking like this:</p>
<pre><code>test-suite doctest
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: DocTest.hs
build-depend: base &gt;= 4.7 &amp;&amp; &lt; 5
, &lt;YOUR_LIBRARY&gt;
, Glob &gt;= 0.7
, doctest &gt;= 0.9.12</code></pre>
<p>and in <code>test/DocTest.hs</code> simply use</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb4-1" title="1"><span class="kw">module</span> <span class="dt">Main</span> <span class="kw">where</span></a>
<a class="sourceLine" id="cb4-2" title="2"></a>
<a class="sourceLine" id="cb4-3" title="3"><span class="kw">import</span> <span class="dt">DocTest</span></a>
<a class="sourceLine" id="cb4-4" title="4"><span class="kw">import</span> <span class="dt">System.FilePath.Glob</span> (glob)</a>
<a class="sourceLine" id="cb4-5" title="5"></a>
<a class="sourceLine" id="cb4-6" title="6">main <span class="ot">=</span> glob <span class="st">&quot;src/**/*.hs&quot;</span> <span class="op">&gt;&gt;=</span> docTest</a></code></pre></div>
<p>Now <code>stack test</code> or <code>cabal test</code> will check the validity of your documentation.</p>
<h2 id="bonuses">Bonuses</h2>
<h3 id="verifying-documentation-coverage">Verifying documentation coverage</h3>
<ol type="1">
<li>Install haddock <code>stack install haddock</code> or <code>cabal install haddock</code></li>
<li>Launch haddock without output format:</li>
</ol>
<pre><code>&gt; haddock src/**/*.hs
Haddock coverage:
100% ( 15 / 15) in 'Data.Duration'
100% ( 3 / 3) in 'Data.Duration.Tutorial'</code></pre>
<h3 id="continuous-integration">Continuous Integration</h3>
<p>There are plenty of alternative solution. I provide the one I believe would be used by most people. So if you use <code>github</code> simply create an account on <a href="http://travis-ci.org"><code>travis</code></a>.</p>
<p>Add a <code>.travis.yml</code> file in your repo containing the content of the file <a href="http://docs.haskellstack.org/en/stable/GUIDE/#travis-with-caching">here</a> and remove the builds you dont need. It will build your project using a lot of different GHC versions and environemnts.</p>
<p>If you are afraid by such its complexity you might just want to use this one:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode .yaml"><code class="sourceCode yaml"><a class="sourceLine" id="cb6-1" title="1"><span class="fu">sudo:</span><span class="at"> </span><span class="ch">false</span></a>
<a class="sourceLine" id="cb6-2" title="2"></a>
<a class="sourceLine" id="cb6-3" title="3"><span class="fu">addons:</span></a>
<a class="sourceLine" id="cb6-4" title="4"> <span class="fu">apt:</span></a>
<a class="sourceLine" id="cb6-5" title="5"> <span class="fu">packages:</span></a>
<a class="sourceLine" id="cb6-6" title="6"> <span class="kw">-</span> libgmp-dev</a>
<a class="sourceLine" id="cb6-7" title="7"></a>
<a class="sourceLine" id="cb6-8" title="8"><span class="co"># Caching so the next build will be fast too.</span></a>
<a class="sourceLine" id="cb6-9" title="9"><span class="fu">cache:</span></a>
<a class="sourceLine" id="cb6-10" title="10"> <span class="fu">directories:</span></a>
<a class="sourceLine" id="cb6-11" title="11"> <span class="kw">-</span> $HOME/.stack</a>
<a class="sourceLine" id="cb6-12" title="12"></a>
<a class="sourceLine" id="cb6-13" title="13"><span class="fu">before_install:</span></a>
<a class="sourceLine" id="cb6-14" title="14"><span class="co"># Download and unpack the stack executable</span></a>
<a class="sourceLine" id="cb6-15" title="15"><span class="kw">-</span> mkdir -p ~/.local/bin</a>
<a class="sourceLine" id="cb6-16" title="16"><span class="kw">-</span> export PATH=$HOME/.local/bin:$PATH</a>
<a class="sourceLine" id="cb6-17" title="17"><span class="kw">-</span> travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin <span class="st">'*/stack'</span></a>
<a class="sourceLine" id="cb6-18" title="18"></a>
<a class="sourceLine" id="cb6-19" title="19"><span class="fu">script:</span></a>
<a class="sourceLine" id="cb6-20" title="20"> <span class="kw">-</span> stack setup &amp;&amp; stack --no-terminal --skip-ghc-check test</a></code></pre></div>
<p>Dont forget to activate your repo in travis.</p>
<p>For some bonus points add the build status badge in your <code>README.md</code> file:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode .markdown"><code class="sourceCode markdown"><a class="sourceLine" id="cb7-1" title="1"><span class="ot">[![Build Status](https://travis-ci.org/user-name/project-name.svg?branch=master)</span>](https://travis-ci.org/user-name/project-name)</a></code></pre></div>
<p>Congratulation! Now if you break your documentation examples, youll get notified.</p>
<h3 id="badges">Badges</h3>
<p>You could add badges to your <code>README.md</code> file.</p>
<p>Here is a list of some: <a href="http://shields.io"><code>shields.io</code></a></p>
<h4 id="hackage">Hackage</h4>
<div class="sourceCode" id="cb8"><pre class="sourceCode .markdown"><code class="sourceCode markdown"><a class="sourceLine" id="cb8-1" title="1"><span class="ot">[![Hackage](https://img.shields.io/hackage/v/packagename)</span>](https://hackage.haskell.org/package/packagename)</a></code></pre></div>
<h4 id="stackage">Stackage</h4>
<p>If you didnt declared your package to <code>stackage</code>, please do it. It isnt much work. Just edit a file to add your package. And youll could be able to add another badge:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode .markdown"><code class="sourceCode markdown"><a class="sourceLine" id="cb9-1" title="1"><span class="ot">[![packagename on Stackage LTS](http://stackage.org/package/packagename/badge/lts-3)</span>](http://stackage.org/lts/package/packagename)</a></code></pre></div>
<p>See <a href="https://www.fpcomplete.com/blog/2015/10/stackage-badges">Stackage Badges</a> for more informations.</p>
<h3 id="creating-a-new-project-with-stack">Creating a new project with <code>stack</code></h3>
<p>If you use <code>stack</code> I suggest you to use the <code>tasty-travis</code> template. It will include the boilerplate for:</p>
<ul>
<li>tests</li>
<li>doctest</li>
<li>benchmark</li>
<li>travis CI</li>
<li>a README file to help you start</li>
</ul>
<p>So edit your <code>~/.stack/config.yaml</code> like this:</p>
<pre><code>templates:
params:
author-name: Your Name
author-email: your@mail.com
copyright: 'Copyright: (c) 2016 Your Name'
github-username: yourusername
category: Development</code></pre>
<p>And then you can create a new projec with:</p>
<pre><code>stack new my-project tasty-travis</code></pre>
<h2 id="generated-documentation">Generated Documentation</h2>
<p>Even not doing anything, if you submit your library to hackage, haddock should generate some API documentation for free.</p>
<p>But to make <em>real</em> documentation you <em>need</em> to add some manual annotations.</p>
<p><strong>Functions</strong>:</p>
<pre><code><span class="highlight">-- | My function description</span>
myFunction :: T1 <span class="highlight">-- ^ arg1 description</span>
-&gt; T2 <span class="highlight">-- ^ arg2 description</span>
myFunction arg1 arg2 = ...</code></pre>
<p><strong>Data</strong>:</p>
<pre><code>data MyData a b
= C1 a b -- ^ doc for constructor C1
| C2 a b -- ^ doc for constructor C2
data MyData a b
= C { a :: TypeA <span class="highlight">-- ^ field a description</span>
, b :: TypeB <span class="highlight">-- ^ field b description</span>
}</code></pre>
<p><strong>Module</strong>:</p>
<pre><code>{-|
Module : MyModule
Description: Short description
Copyright : (c)
License : MIT
Here is a longer description of this module.
With some code symbol @MyType@.
And also a block of code:
@
data MyData = C Int Int
myFunction :: MyData -&gt; Int
@
-}</code></pre>
<p><strong>Documentation Structure</strong>:</p>
<pre><code>module MyModule (
-- * Classes
C(..),
-- * Types
-- ** A data type
T,
-- ** A record
R,
-- * Some functions
f, g
) where</code></pre>
<p>That will generate headings.</p>
<h2 id="other-random-ideas">Other Random Ideas</h2>
<p>In Haskell we have great tools like <a href="http://hayoo.fh-wedel.de"><code>hayoo!</code></a> and <a href="https://www.haskell.org/hoogle/"><code>hoogle</code></a>.</p>
<p>And <code>hackage</code> and <code>stackage</code> provide also a lot of informations.</p>
<p>But generally we lack a lot of Tutorials and Guides. This post was an attempt to help people making more of them.</p>
<p>But there are other good ideas to help improve the situation.</p>
<h3 id="create-a-doc-with-link-to-best-practices">Create a doc with link to best practices</h3>
<p>In clojure when you create a new project using <code>lein new my-project</code> a directory <code>doc</code> is created for you. It contains a file with a link to this blog post:</p>
<ul>
<li><a href="https://jacobian.org/writing/what-to-write/">What to write</a></li>
</ul>
<h3 id="having-a-page-by-functionsymbol-with-comments">Having a page by function/symbol with comments</h3>
<p>If you try to search for some clojure function on a search engine there is a big chance the first result will link to:</p>
<ul>
<li><a href="http://clojuredocs.org"><code>clojuredocs.org</code></a>: try to search for <code>reduce</code>, <code>update-in</code> or <code>index</code> for example</li>
</ul>
<p>For each symbol necessiting a documentation. You dont only have the details and standard documentation. Youll also get:</p>
<ul>
<li>Responsive Design (sometime you want to look at documentation on a mobile)</li>
<li>Contributed Examples</li>
<li>Contributed See Also section</li>
<li>Contributed notes/comments</li>
</ul>
<p><a href="http://clojuredocs.org"><code>clojuredocs.org</code></a> is an independant website from the official Clojure website.</p>
<p>Most of the time, if you google the function you search you end up on <a href="http://clojuredocs.org">clojuredocs</a> for wich there are many contributions.</p>
<p>Currently stackage is closer to these feature than hackage. Because on stackage you have access to the README and also some comments by package.</p>
<p>I believe it would be more efficient to have at least a page by module and why not a page by <em>symbol</em> (data, functions, typeclasses…).</p>
<p>For example, we could provide details about <code>foldl</code> for example. Also as there would be less information to display, it will make the design cleaner.</p>
<p>Today, if you want to help documenting, you need to make a PR to the source of some library. While if we had an equivalent to clojuredocs for Haskell, adding documentation would simply be a few clicks away:</p>
<ol type="1">
<li>login</li>
<li>add/edit some example, comments, see-also section</li>
</ol>
<p>There are more than 23k people on <code>/r/haskell</code>. If only 1% of them would take 10 minutes adding a bit of documentation it will certainly change a lot of things in the percieved documentation quality.</p>
<p>And last but not least,</p>
<h2 id="design-is-important"><strong>Design is important</strong></h2>
<div>
<img src="../../../../Scratch/img/blog/Haskell-Tutorials--a-tutorial/design_is_important.jpg" alt="Design is Important" />
</div>
<p>Design is a vague word. A good design should care not only about how something look, but also how users will interact with it. For example by removing things to focus on the essential.</p>
<p>When I stumble upon some random blog post or random specification in the Haskell community, I had too much a feeling of old fashioned design.</p>
<p>If you look at node.js community lot of their web page look cleaner, easier to read and in the end, more user friendly.</p>
<p>Haskell is very different from node, I wouldnt like to replace all long and precise documentation with short human unprecise concepts. I dont want to transform scientific papers by tweets.</p>
<p>But like the scientific community has upgraded with the use of LaTeX, I believe we could find something similar that would make, very clean environment for most of us. A kind of look and feel that will be</p>
<ul>
<li>modern</li>
<li>device friendly (either on computer, mobile, tablet)</li>
<li>efficient, focus on what is most important and is helpful</li>
</ul>
</div>
<div id="afterarticle">
<div id="social">
<a href="../../../../Scratch/en/blog/feed/feed.xml" target="_blank" rel="noopener noreferrer nofollow" class="social">RSS</a>
·
<a href="https://twitter.com/home?status=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Haskell-Tutorials--a-tutorial/%20via%20@yogsototh" target="_blank" rel="noopener noreferrer nofollow" class="social">Tweet</a>
·
<a href="http://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Haskell-Tutorials--a-tutorial/" target="_blank" rel="noopener noreferrer nofollow" class="social">FB</a>
<br />
<a class="message" href="../../../../Scratch/en/blog/Social-link-the-right-way/">These social sharing links preserve your privacy</a>
</div>
<div id="navigation">
<a href="../../../../">Home</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/blog">Blog</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/softwares">Softwares</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/about">About</a>
</div>
<div id="totop"><a href="#header">↑ Top ↑</a></div>
<div id="bottom">
<div>
Published on 2016-05-06
</div>
<div>
<a href="https://twitter.com/yogsototh">Follow @yogsototh</a>
</div>
<div>
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US">Yann Esposito©</a>
</div>
<div>
Done with
<a href="http://www.vim.org" target="_blank" rel="noopener noreferrer nofollow"><strike>Vim</strike></a>
<a href="http://spacemacs.org" target="_blank" rel="noopener noreferrer nofollow">spacemacs</a>
<span class="pala">&amp;</span>
<a href="http://nanoc.ws" target="_blank" rel="noopener noreferrer nofollow"><strike>nanoc</strike></a>
<a href="http://jaspervdj.be/hakyll" target="_blank" rel="noopener noreferrer nofollow">Hakyll</a>
</div>
<hr />
<div style="max-width: 100%">
<a href="https://cardanohub.org">
<img src="../../../../Scratch/img/ada-logo.png" class="simple" style="height: 16px;
border-radius: 50%;
vertical-align:middle;
display:inline-block;" />
ADA:
</a>
<code style="display:inline-block;
word-wrap:break-word;
text-align: left;
vertical-align: top;
max-width: 85%;">
DdzFFzCqrhtAvdkmATx5Fm8NPJViDy85ZBw13p4XcNzVzvQg8e3vWLXq23JQWFxPEXK6Kvhaxxe7oJt4VMYHxpA2vtCFiP8fziohN6Yp
</code>
</div>
</div>
</div>
</div>
</div>
</body>
</html>