350 lines
23 KiB
HTML
350 lines
23 KiB
HTML
<!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="/css/y.css" />
|
||
<link rel="stylesheet" type="text/css" href="/css/legacy.css" />
|
||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.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://ieji.de/@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 wasn’t 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"> >>> humanReadableDuration 1002012.002</span></a>
|
||
<a class="sourceLine" id="cb1-22" title="22"><span class="co"> "11 days 14 hours 20 min 12s 2ms"</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: <<path-to-image.png title>></span></a>
|
||
<a class="sourceLine" id="cb1-29" title="29"><span class="co">and links: <http://haskell-lang.org haskell>.</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">> 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 you’ll do a <code>stack test</code> or <code>cabal test</code> you’ll get errors if some example doesn’t 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">-- >>> 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">-></span> <span class="dt">Int</span> <span class="ot">-></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 >= 4.7 && < 5
|
||
, <YOUR_LIBRARY>
|
||
, Glob >= 0.7
|
||
, doctest >= 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">"src/**/*.hs"</span> <span class="op">>>=</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>> 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 don’t 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 && stack --no-terminal --skip-ghc-check test</a></code></pre></div>
|
||
<p>Don’t 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, you’ll 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 didn’t declared your package to <code>stackage</code>, please do it. It isn’t much work. Just edit a file to add your package. And you’ll 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>
|
||
-> 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 -> 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 don’t only have the details and standard documentation. You’ll 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 wouldn’t like to replace all long and precise documentation with short human unprecise concepts. I don’t 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="/rss.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="/about-me.html">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://ieji.de/@yogsototh">Follow @yogsototh@ieji.de</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">&</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>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|