<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>AlternativeBit</title>
    <link>https://alternativebit.fr/posts/</link>
    <description>Recent posts</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 30 Oct 2023 10:00:00 +0200</lastBuildDate>
    <atom:link href="https://alternativebit.fr/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Nsncd, Anniversary Updates</title>
      <link>https://alternativebit.fr/posts/nixos/nsncd-some-updates/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/system-programming">System programming</category><category domain="https://alternativebit.fr/tags/nix">Nix</category>
      <pubDate>Mon, 30 Oct 2023 10:00:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/nixos/nsncd-some-updates/</guid>
      <description>&lt;p&gt;Last year, &lt;a href=&#34;https://flokli.de/&#34;&gt;flokli&lt;/a&gt; and I worked towards re-using
TwoSigma&amp;rsquo;s Nsncd as the main NixOS Nscd daemon.&lt;/p&gt;
&lt;p&gt;What is that even about? Well, Nscd is a Glibc daemon that was
originally meant to cache the host/user resolution requests. It&amp;rsquo;s
mostly obsolete by now, but on NixOS and Guix, we abuse this daemon to
get a stable ABI to load the host NSS modules from a potentially
different glibc version. If you have no idea what I&amp;rsquo;m talking about
here and want to read more about that context, you should probably
read &lt;a href=&#34;https://flokli.de/posts/2022-11-18-nsncd/&#34;&gt;flokli&amp;rsquo;s original release blog
post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s now been a year since we released the Nsncd host lookups. Since
then, Nsncd is used by default on NixOS in place of Nscd. The
migration has been mostly bugless. Emphasis on the &lt;strong&gt;mostly&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;While this switch has been beneficial for most of us, getting rid of
the &amp;ldquo;too-much-caching&amp;rdquo; numerous bugs, some unexpected side effects
appeared. Such as
&lt;a href=&#34;https://github.com/NixOS/nixpkgs/issues/196934&#34;&gt;breaking&lt;/a&gt; the
&lt;code&gt;hostname --fqdn&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Recently, flokli and I ended up under the same Catalonian roof for a
Numtide-organized programming retreat. Kudos to
&lt;a href=&#34;https://zimbatm.com/&#34;&gt;Zimbatm&lt;/a&gt; who handled most of the thankless
logistics to make this happen &amp;lt;3&lt;/p&gt;
&lt;p&gt;After almost a full year of procrastination, turns out that a 10-hour
session of focused work was all it took to &lt;strong&gt;almost&lt;/strong&gt; fix these
long-lingering issues.&lt;/p&gt;
&lt;p&gt;Wait, why did I write almost here? Ah yes. After these 10 initial
hours, I kept uncovering a long series of subtle yet real bugs for
the next 4 days. Turns out that the third full rewrite of the fix was
the charm!&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;What was the root cause of these faulty host resolutions in the end?&lt;/p&gt;
&lt;p&gt;Turns out that, even if the &lt;code&gt;gethostbyname&lt;/code&gt; and &lt;code&gt;gethostbyaddr&lt;/code&gt; libc
functions have been long deprecated in favor of &lt;code&gt;getaddrinfo&lt;/code&gt;, some
pretty critical pieces of software are still using them. You guessed
it: the &lt;code&gt;hostname&lt;/code&gt; command still uses those.&lt;/p&gt;
&lt;p&gt;We originally decided to implement these two functions through
&lt;code&gt;getaddrinfo&lt;/code&gt;. This was a mistake: the legacy operations do not
behave exactly like the new ones.&lt;/p&gt;
&lt;p&gt;To fix this bug, we wrote some FFI bindings for these two legacy
functions. Then, we replaced our dodgy &lt;code&gt;gethostbyname&lt;/code&gt; and
&lt;code&gt;gethostbyaddr&lt;/code&gt; implementation in Nsncd to use these bindings.&lt;/p&gt;
&lt;p&gt;In the meantime, flokli wrote
&lt;a href=&#34;https://github.com/flokli/sockburp&#34;&gt;sockburp&lt;/a&gt;, a debug tool for Unix
sockets. This has been a game changer to detect the wire format
compatibility issues. You can read more about this tool in &lt;a href=&#34;https://flokli.de/posts/2023-10-30-sockburp/&#34;&gt;this
blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We have not managed to upstream all our changes to the TwoSigma repo
yet. NixOS Nsncd is still hosted from our
&lt;a href=&#34;https://github.com/nix-community/nsncd/&#34;&gt;nix-community&lt;/a&gt; fork. We did
not give up on upstreaming it though and hope to do that in the next
few months.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s pretty much all. Until next time, happy and serene hacking
folks :)&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Ah, you&amp;rsquo;re here for the gory details? Here you go, read
&lt;a href=&#34;https://github.com/nix-community/nsncd/pull/9&#34;&gt;these&lt;/a&gt;
&lt;a href=&#34;https://github.com/nix-community/nsncd/pull/10&#34;&gt;two&lt;/a&gt; PRs history.
My treat!&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>The Nix, OpenGL and Ubuntu Integration Nightmare</title>
      <link>https://alternativebit.fr/posts/nixos/nix-opengl-and-ubuntu-integration-nightmare/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/nix">Nix</category>
      <pubDate>Thu, 20 Apr 2023 10:00:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/nixos/nix-opengl-and-ubuntu-integration-nightmare/</guid>
      <description>&lt;p&gt;In this article, we&amp;rsquo;re about to dive into the uncharted OpenGL on Linux waters. After briefly explaining how the OpenGL calls are routed from your application to the GPU, we&amp;rsquo;ll look at the NixOS special case. We’ll then explore how we can run the OpenGL programs built by Nix on a foreign distribution, such as Ubuntu or Fedora. Finally, we’ll introduce &lt;a href=&#34;https://github.com/numtide/nix-gl-host&#34;&gt;NixGLHost&lt;/a&gt; a new approach to solve this problem.&lt;/p&gt;
&lt;h2 id=&#34;openglcuda-on-linux&#34;&gt;OpenGL/CUDA on Linux?&lt;/h2&gt;
&lt;p&gt;Everybody, including your favorite pet play video games. Your favorite go-to desktop application is relying on GPU acceleration to render on your screen, and AI is taking over the world. Welcome to 2023. In the last 15 years, GPUs transitioned from being this gamer-specific niche hardware to being this globally-used parrallel platform.&lt;/p&gt;
&lt;p&gt;There are a lot of GPU-related APIs in the wild. OpenGL, OpenCL, Vulkan, DirectX, Metal, etc.. In this article, we&amp;rsquo;ll focus on a very small subset of them, OpenGL and CUDA. Even more precisely, OpenGL and CUDA on Linux. How are these two APIs working?&lt;/p&gt;
&lt;p&gt;Linux being Linux, there&amp;rsquo;s no simple answer to that. In most setups, and for OpenGL alone, there&amp;rsquo;s no big unified API. There are actually two concurrent ones, acting as an interface between the actual OpenGL program and the desktop environment.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://registry.khronos.org/OpenGL/index_gl.php#apispecs&#34;&gt;GLX&lt;/a&gt;&lt;/strong&gt;: the legacy one, a Xorg-specific API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://www.khronos.org/egl/&#34;&gt;EGL&lt;/a&gt;&lt;/strong&gt;: the new kid in town. The interface is used by Wayland and Android among others.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To be clear: these are API specifications, not implementations. For these two APIs we have multiple implementations. Some are proprietary, and directly written by the hardware vendors. Some others are open source, sometimes implemented by hardware vendors, sometimes by courageous volunteers.&lt;/p&gt;
&lt;p&gt;There are currently two major OpenGL implementations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://www.mesa3d.org/&#34;&gt;Mesa&lt;/a&gt;&lt;/strong&gt;: the main OpenSource implementation. It&amp;rsquo;s actually more of a meta-driver, it includes drivers for a wide range of hardware: Intel integrated GPUs, AMD discrete GPUs, some ARM Mali, and a lot more.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NVIDIA&lt;/strong&gt;: a proprietary implementation, maintained by Nvidia itself. That&amp;rsquo;s the optimal way to drive your Nvidia graphics card on Linux.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These drivers are usually split into two parts. A thin kernel-space part is in charge of the low-level communication with the GPU, and a thicker user-space part is in charge of translating the OpenGL instructions into something the small kernel-space part can understand. Conceptually speaking, the kernel space part acts as a proxy to the actual hardware, nothing more.&lt;/p&gt;
&lt;p&gt;These driver stacks, on top of providing the low-level code in charge of talking to the GPU, are supplying implementations for various interfaces. Among those, are GLX and EGL. To sum things up, you have a GLX and EGL implementation for each and every driver. EG. &lt;code&gt;libGLX_nvidia.so&lt;/code&gt;, &lt;code&gt;libGLX_mesa.so&lt;/code&gt;, etc.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey, you&amp;rsquo;re lying! There is a unified API!&lt;/p&gt;
&lt;p&gt;I already used OpenGL, I know for a fact that my program is linked against &lt;code&gt;libGL.so&lt;/code&gt;, not to one of these weird-ass API!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You&amp;rsquo;re absolutely right! But what if I told you that &lt;code&gt;libGL.so&lt;/code&gt; is actually a lie?&lt;/p&gt;
&lt;p&gt;I omitted  an important part of this OpenGL stack on purpose: &lt;a href=&#34;https://gitlab.freedesktop.org/glvnd/libglvnd&#34;&gt;libglvnd&lt;/a&gt;. Originally developed by NVIDIA before being maintained by the freedesktop community, this library aims to be the thing your library is going to link against. It&amp;rsquo;s not doing anything by itself, it just dispatches the OpenGL calls to the most appropriate APIs. Through various heuristics, libglvnd figures out through which interface the OpenGL instructions should go. Roughly speaking, if the target surface is rendered through Wayland, it&amp;rsquo;ll use the EGL implementation. If it&amp;rsquo;s rendered through Xorg, the GLX implementation. It also uses some more heuristics and config files to figure out which driver (Mesa, Nvidia, etc.) should be used.&lt;/p&gt;
&lt;p&gt;What about CUDA? Well, for this one, we don&amp;rsquo;t really have to bother with interoperability; NVIDIA is controlling the whole chain, from the user-space SDK to the silicon after all. The overall call graph is much simpler, although harder to integrate. Your CUDA program is linked against the &lt;code&gt;libcuda.so&lt;/code&gt; shared library. This library directly communicates with the thin kernel-space driver in charge to send the CUDA operations to the actual GPU.&lt;/p&gt;
&lt;p&gt;Overall, from a bird&amp;rsquo;s-eye view, the overall OpenGL graphic stack looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/nix-gl-host/GL-dispatch-bare.svg&#34; alt=&#34;Diagram summarizing this paragraph. It shows how the OpenGL program, libglvnd, the EGL, and GLX implementation interact with each other.&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;how-does-nix-deal-with-that&#34;&gt;How Does Nix Deal With That?&lt;/h2&gt;
&lt;p&gt;Now that we roughly see how the high-level components fit together in the Linux space, we know there&amp;rsquo;s a layer dynamically dispatching the graphics calls to the most relevant driver depending on the current context. It works reasonably well. Well enough to power most of the modern days GPU-fueled AI revolution. Sadly, the cost of dynamic dispatching is a loss of determinism.&lt;/p&gt;
&lt;h3 id=&#34;nixos&#34;&gt;NixOS&lt;/h3&gt;
&lt;p&gt;If your heart rate bumped slightly when reading the previous paragraph, chances are you already drank the NixOS kool-aid and are anticipating a fair share of pain integrating this with NixOS. And for good reason! How can we expect a statically-defined Nix closure to work on each and every end-user GPU? We surely can&amp;rsquo;t include all the existing drivers in the closure, it&amp;rsquo;d be massive!&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t worry too much though, we don&amp;rsquo;t have to get that hardcore. Spoiler alert: I&amp;rsquo;m about to uncover a dirty-hack. You know, the dirty-hack rendering this very web-page (if you&amp;rsquo;re reading this on NixOS).&lt;/p&gt;
&lt;p&gt;NixOS has a magical &lt;code&gt;/run/opengl-driver&lt;/code&gt; directory. In this directory, you can find all the libs you&amp;rsquo;ll need to drive the GPU attached to your machine. OpenGL, Vulkan rendering, OpenCL computing, VA-API video acceleration. You name it!&lt;/p&gt;
&lt;p&gt;This directory — we&amp;rsquo;ll call it &amp;ldquo;link farm&amp;rdquo; from now on, because, well, that&amp;rsquo;s what it really is! — is defined on a system level via the &lt;code&gt;hardware.opengl&lt;/code&gt; &lt;a href=&#34;https://github.com/NixOS/nixpkgs/blob/45ce4de0fb6a9a9f1c9d4f675369a743e7c7729d/pkgs/build-support/add-opengl-runpath/default.nix#L3&#34;&gt;NixOS module&lt;/a&gt;. Overall, it means the GPU driver is defined through the NixOS &lt;strong&gt;system&lt;/strong&gt; closure, and exposed at runtime through the &lt;code&gt;/run/opengl-driver&lt;/code&gt; link farm. The OpenGL program derivations have no knowledge of the actual OpenGL implementation. They simply link to &lt;code&gt;libGL.so&lt;/code&gt; AKA. libglvnd, the generic dispatch layer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Okay, great, we have the drivers in a weird &lt;code&gt;/run/opengl-driver&lt;/code&gt; location containing a link farm pointing to the actual GPU driver.&lt;/p&gt;
&lt;p&gt;Now, how can my Nix-built OpenGL program find these libraries?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, it&amp;rsquo;s Nixpkgs! Obviously, we&amp;rsquo;re injecting them through a wrapper. &lt;a href=&#34;https://github.com/NixOS/nixpkgs/blob/45ce4de0fb6a9a9f1c9d4f675369a743e7c7729d/pkgs/build-support/add-opengl-runpath/default.nix#L3&#34;&gt;The wrapper we&amp;rsquo;re talking about&lt;/a&gt; is called &lt;code&gt;addOpenGLRunpath&lt;/code&gt;. It injects &lt;code&gt;/run/opengl-drivers&lt;/code&gt; to the library path through an &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; entry. At runtime, Libglvnd will load these libraries and dispatch them the OpenGL calls.&lt;/p&gt;
&lt;p&gt;On a foreign distribution, where &lt;code&gt;/run/opengl-driver&lt;/code&gt; won&amp;rsquo;t exist, the dynamic loader simply ignores this library path entry. The wrapper essentially acts as a no-op on a non-NixOS distribution.&lt;/p&gt;
&lt;p&gt;In a nutshell:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NixOS&lt;/strong&gt; stores the host system GPU drivers in a well-known directory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nixpkgs&lt;/strong&gt; injects this well-known directory to the OpenGL program library path through a wrapper.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-about-running-nix-on-a-foreign-distro&#34;&gt;What About Running Nix on a Foreign Distro?&lt;/h3&gt;
&lt;p&gt;Now, how would you run a Nix OpenGL program on a foreign Linux distribution such as Ubuntu?&lt;/p&gt;
&lt;p&gt;All of a sudden there is no more &lt;code&gt;/run/opengl-driver&lt;/code&gt;. This is where it gets hairy!&lt;/p&gt;
&lt;p&gt;The go-to solution these days is &lt;a href=&#34;https://github.com/guibou/nixGL&#34;&gt;NixGL&lt;/a&gt;. It&amp;rsquo;s a wrapper you apply on top of an OpenGL derivation. With a few clever heuristics, it figures out which driver your host system expects at &lt;strong&gt;build-time&lt;/strong&gt;. It then builds the relevant driver derivation (Mesa, NVidia, Bumblebee) and generates a wrapper injecting the driver dynamic libraries to the OpenGL process load path.&lt;/p&gt;
&lt;p&gt;Remember what I told you during the introduction: The GPU driver is split between a thick user-space library and a thin kernel-space module. What we have here is only the user-space side of the story. The user-space part of the driver is distributed through Nix, the kernel-space one by the host distribution. One might fear hitting some protocol-level incompatibilities between the user space driver and the kernel-space one. And for good reason! It could definitely happen. In practice though, it&amp;rsquo;s good enough: the NixGL heuristics tend to be fairly clever at figuring out which userspace library to use.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s definitely a tradeoff. The GPU driver ends up living in the Nix closure. All of sudden, your Nix closure is tailored towards a specific GPU and won&amp;rsquo;t be generic anymore. This approach requires you to precisely know the target hardware specification during the Nix build time. If you want to deploy an application on N computers, you&amp;rsquo;ll have to conceptually &lt;code&gt;nix build&lt;/code&gt; the application N times. You&amp;rsquo;ll need a detailed inventory of the hardware you want to deploy on, or have the build happening on the end-user machine, which is not always possible.&lt;/p&gt;
&lt;h2 id=&#34;kludgy-mikey-likey&#34;&gt;Kludgy! Mikey Likey!&lt;/h2&gt;
&lt;p&gt;Could we have an alternate approach to solve this Nix-OpenGL-on-a-foreign-distro problem? An alternative not requiring us to include the OpenGL driver part of the deployed closure, but rather have this managed by the host distribution? Kind of what we have on NixOS?&lt;/p&gt;
&lt;p&gt;I think it&amp;rsquo;s fair to assume the host distribution is smart enough to properly set up the graphic stack. What if we glued the host user-space GPU environment to the Nix program instead of using a Nixpkgs-provided one? This thought was the starting point of &lt;a href=&#34;https://github.com/numtide/nix-gl-host&#34;&gt;NixGLHost&lt;/a&gt;, a new way of running your OpenGL Nix programs on a foreign distro.&lt;/p&gt;
&lt;p&gt;This approach is pretty Nix-heretic. We&amp;rsquo;re injecting some host DSOs&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; into the Nix closure. Nix is specifically designed to limit this kind of interaction with the host system! That being said, we&amp;rsquo;re being heretic but cautious: we only inject the entry points for EGL/GLX/CUDA. We patch the entry points&amp;rsquo; ELF runpaths to point to a place containing their own dependencies. This acts as a minimal sandboxing setup.&lt;/p&gt;
&lt;p&gt;I won&amp;rsquo;t dive too much into the implementation details here, you can check out the &lt;a href=&#34;https://github.com/numtide/nix-gl-host/blob/main/INTERNALS.md&#34;&gt;internals.md&lt;/a&gt; file if you&amp;rsquo;re interested in the nitty-gritty details.&lt;/p&gt;
&lt;p&gt;Overall, this experimental new wrapper re-uses the drivers provisioned by your host distribution. The kernel-space module and user-space library are both provisioned by the host system, we&amp;rsquo;re sure they won&amp;rsquo;t get out of sync. The graphic driver stays out of the Nix closure, the closure stays truly generic and will work on any GPU.&lt;/p&gt;
&lt;p&gt;This experiment turned out to work great for the use cases we tested it against so far. I managed to run DaVinci Resolve and a couple of videogames built by Nix on a Ubuntu machine. A customer is actually already using the prototype in production&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id=&#34;whats-next-for-nixglhost&#34;&gt;What&amp;rsquo;s Next for NixGLHost?&lt;/h3&gt;
&lt;p&gt;The next obvious step is to extend the supported OpenGL implementations. At the moment, we only support the NVIDIA proprietary driver. It&amp;rsquo;d be great to support Mesa as well. Technically, it&amp;rsquo;s not a hard thing to do. Sadly, the Python prototype codebase is quite messy and difficult to extend as it is.&lt;/p&gt;
&lt;p&gt;The wrapper is also pretty slow to run, ~200ms on a hot cache. Most of this time is spent spinning up the CPython interpreter. Starting a whole interpreter for a very short-lived program turned out to be very costly, who could have guessed! Should we embrace the meme and &lt;a href=&#34;https://www.urbandictionary.com/define.php?term=riir&#34;&gt;RIIR&lt;/a&gt; the project? I personally think it might be a good idea and started to work on that.&lt;/p&gt;
&lt;p&gt;We also should think about improving the sandboxing. At the moment, we&amp;rsquo;re injecting the host DSOs&lt;sup id=&#34;fnref1:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; symbols directly into the program&amp;rsquo;s global symbol table. If, by misfortune, the Nix program ends up using the same library as the GPU driver but with a slightly incompatible version, all hell breaks loose! There are some ways to go around that, we could leverage the &lt;code&gt;dlmopen&lt;/code&gt; function to load the driver DSOs&lt;sup id=&#34;fnref2:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; to another namespace. We could potentially leverage &lt;a href=&#34;https://gitlab.collabora.com/vivek/libcapsule&#34;&gt;libcapsule&lt;/a&gt; to perform such a &amp;ldquo;namespaced&amp;rdquo; dynamic library loading.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see how it goes, I&amp;rsquo;ll probably post a follow-up here when we&amp;rsquo;ll reach the next major milestone :)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Greets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://flokli.de/&#34;&gt;flokli&lt;/a&gt; for enduring my terrible hacks and brainstorming this problem space with me &amp;lt;3&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://numtide.com/&#34;&gt;Numtide&lt;/a&gt; and &lt;a href=&#34;https://ottomotors.com/&#34;&gt;Otto Motors&lt;/a&gt; for trusting me on this and funding most of the NixGLHost work &amp;lt;3&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/guibou&#34;&gt;Guibou&lt;/a&gt; for writing &lt;a href=&#34;https://github.com/guibou/nixGL&#34;&gt;NixGL&lt;/a&gt; &amp;lt;3&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://alexarenoire.medium.com/&#34;&gt;Alex&lt;/a&gt; and &lt;a href=&#34;https://tazj.in/&#34;&gt;tazjin&lt;/a&gt; for proof-reading this article &amp;lt;3&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Dynamic_linker&#34;&gt;Dynamic Shared Object&lt;/a&gt;, a shared library.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref1:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref2:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;This is an alpha release, I&amp;rsquo;m not saying you should use it in production as well. Use this with caution, it may eat your kittens!&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>Nix Substitution: the Way Forward</title>
      <link>https://alternativebit.fr/posts/nixos/future-of-nix-substitution/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/nix">Nix</category><category domain="https://alternativebit.fr/tags/guix">Guix</category>
      <pubDate>Thu, 31 Mar 2022 08:18:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/nixos/future-of-nix-substitution/</guid>
      <description>&lt;h2 id=&#34;abstract&#34;&gt;Abstract&lt;/h2&gt;
&lt;p&gt;Nix and Guix have the unfortunate reputation to require a lot of bandwidth to distribute software. This reputation is sadly grounded. It seems like explicitly pointing to your dependencies comes with an overhead cost in terms of download size. Or does it?&lt;/p&gt;
&lt;p&gt;Currently, both Guix and Nix can substitute pre-built store paths from a binary cache using the Nix Archive (NAR) format. After conducting several benchmarks, we conclude that the substitution granularity should be finer to improve the substitution performance. We can increase the substitution granularity either by substituting files individually or by substituting chunked individual files.&lt;/p&gt;
&lt;p&gt;If you are already familiar with the Nix and Guix substitution mechanism, feel free to ignore the next two sections and directly jump to the &lt;a href=&#34;#rethinking-the-substitution-granularity&#34;&gt;&amp;ldquo;Rethinking The Substitution Granularity&amp;rdquo;&lt;/a&gt; one.&lt;/p&gt;
&lt;h2 id=&#34;you-said-substitution&#34;&gt;You Said Substitution?&lt;/h2&gt;
&lt;p&gt;We usually don&amp;rsquo;t want to rebuild the whole world locally. When we want to install Firefox on a laptop, we&amp;rsquo;d rather not build it from the source on the machine itself but instead download a pre-built version from a cache. For this purpose, Nix provides a store path substitution mechanism.&lt;/p&gt;
&lt;p&gt;Instead of building a derivation locally, we substitute the local build with a pre-built artifact stored in a binary cache. The store paths are getting transferred between the binary cache and the local machine using the NAR archive format. This format can be seen as a compressed store path. Meaning we&amp;rsquo;ll have a NAR archive per top-level store path we&amp;rsquo;d like to substitute.&lt;/p&gt;
&lt;p&gt;A NAR is always identified using a hash derived from its content. NARs are content-addressed.&lt;/p&gt;
&lt;p&gt;In contrast to that, both the Nix/Guix derivations and the store paths they generate are input-addressed&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;: their hashes are directly derived from their dependencies hashes.&lt;/p&gt;
&lt;p&gt;You now see the issue: on one hand, we have input-addressed derivations and store paths, on the other hand, content-addressed NARs.&lt;/p&gt;
&lt;p&gt;Starting from an input-addressed derivation hash, how are we supposed to figure out the name of the content-addressed NAR we want to download? In other words, how can we figure out the output hash of a derivation without building it first?&lt;/p&gt;
&lt;p&gt;Introducing the &lt;strong&gt;NARinfo&lt;/strong&gt; file. Let&amp;rsquo;s take an example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;» curl https://cache.nixos.org/r5sjd57x0r07bwgipryaxqdkx1gglhiy.narinfo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;StorePath: /nix/store/r5sjd57x0r07bwgipryaxqdkx1gglhiy-libidn2-2.3.2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;URL: nar/010zh5j819qyz6zai6h2hn3d7i406g00m2g4g62bq7m8g91553yb.nar.xz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Compression: xz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;FileHash: sha256:010zh5j819qyz6zai6h2hn3d7i406g00m2g4g62bq7m8g91553yb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;FileSize: 63248
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NarHash: sha256:1npw0jz1cw4k9x25f2vsdhsa5cf9568j46bpz2768a1izqj5n9lf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NarSize: 260816
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;References: 0z7sqj4pilbqyp45ix5b0mdgn9xlb024-libunistring-0.9.10 r5sjd57x0r07bwgipryaxqdkx1gglhiy-libidn2-2.3.2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Deriver: 0n91syjwrhmng41f8d23ad0sl4a6ic4g-libidn2-2.3.2.drv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Sig: cache.nixos.org-1:G9PxMT0/nd9ELwL3BBeqWtb2ohMiqw4T4FUwFlAU9M0E45mKg77BzL9gJQ3wH4oYtsa61MV5uzNLPFvK8ZbyCA==
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To substitute the path &lt;code&gt;/nix/store/r5sjd57x0r07bwgipryaxqdkx1gglhiy-libidn2-2.3.2&lt;/code&gt; from the &lt;code&gt;cache.nixos.org&lt;/code&gt; binary cache, we&amp;rsquo;ll first have to download its associated NARinfo file. That NARinfo file is addressed using the same input-addressed hash as its associated store path: &lt;code&gt;r5sjd57x0r07bwgipryaxqdkx1gglhiy&lt;/code&gt;. The NARinfo will contain some metadata needed to perform the substitution, but also, and most importantly, the URL from which we can download the content-addressed NAR.&lt;/p&gt;
&lt;p&gt;To sum up, to perform a substitution, the Nix/Guix client will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compute the input-addressed store path of the derivation.&lt;/li&gt;
&lt;li&gt;Fetch the associated &lt;code&gt;.narinfo&lt;/code&gt; from the binary cache it wants to substitute from.&lt;/li&gt;
&lt;li&gt;Download the content-addressed NARs referenced by the &lt;code&gt;.narinfo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Unpack the downloaded NARs to the local Nix store.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;substitution-hell&#34;&gt;Substitution Hell&lt;/h2&gt;
&lt;p&gt;Now that we established how the Nix substitution mechanism works, let&amp;rsquo;s have a thought experiment: what will happen if we bump the version of a derivation living deep in the Nixpkgs/Guix dependency tree? Something like &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;curl&lt;/code&gt; store path being input-addressed, its name will change for sure. Likewise, its dependencies are also input-addressed, their name will also change. Overall, the &lt;code&gt;curl&lt;/code&gt; store path change will propagate throughout all the Nixpkgs/Guix dependency graph.&lt;/p&gt;
&lt;p&gt;You might say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey! That&amp;rsquo;s not a big deal.&lt;/p&gt;
&lt;p&gt;The NARs are content addressed, this change of input hash shouldn&amp;rsquo;t propagate there.&lt;/p&gt;
&lt;p&gt;The binary cache might have to re-build the whole dependency graph, however, outside of curl, the content of the derivations should remain the same. So, in the end, most of the NARs should remain the same. The clients shouldn&amp;rsquo;t have to re-download all the store paths depending on curl.&lt;/p&gt;
&lt;p&gt;We won&amp;rsquo;t have to re-download massive derivations like Firefox and Chromium for a simple curl bump.&lt;/p&gt;
&lt;p&gt;Right?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And you&amp;rsquo;d be right except for one thing: in the Nix/Guix model, everything is explicit. Every time you&amp;rsquo;ll refer to the curl binary, you&amp;rsquo;ll have to explicitly refer to its store path &lt;code&gt;/nix/${inputHash}-curl/bin/curl&lt;/code&gt;. Its &lt;strong&gt;input-addressed&lt;/strong&gt; store path! This sneaky reference will manage to propagate the input-adressed instability to the output-addressed NARs.&lt;/p&gt;
&lt;p&gt;Coming back to your example: both Chromium and Firefox depend on curl. You&amp;rsquo;ll have to re-download both of these massive store path for every curl bump. Hopefully, you won&amp;rsquo;t mind downloading a couple of gigabytes for a sub-kB string change? Do you? :P&lt;/p&gt;
&lt;p&gt;One has to think: could we improve this situation?&lt;/p&gt;
&lt;h2 id=&#34;rethinking-the-substitution-granularity&#34;&gt;Rethinking the Substitution Granularity&lt;/h2&gt;
&lt;p&gt;Content addressing the substitution archives is definitely a good idea. It brings some hash stability to this unstable input-addressed scheme. However, as we saw earlier, because of the store path references, the input-addressed hash instability ends up propagating through the substitution mechanism.&lt;/p&gt;
&lt;p&gt;To improve the substitution stability, we could think about two solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Remove the store references from the NAR and replace them on the fly when decompressing the NAR into the actual Nix/Guix store.&lt;/li&gt;
&lt;li&gt;Increase the substitution granularity.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;rsquo;m going to ignore that first solution for this post and focus on the &amp;ldquo;Increase the substitution granularity&amp;rdquo; solution.&lt;/p&gt;
&lt;p&gt;The Nix and Guix communities came up with two solutions based on this finer granularity substitution idea: &lt;a href=&#34;https://github.com/flokli/nix-casync/&#34;&gt;nix-casync&lt;/a&gt; and a file-based substitution discussed in the &lt;a href=&#34;https://lists.gnu.org/archive/html/guix-devel/2021-01/msg00079.html&#34;&gt;Guix mailing list&lt;/a&gt;.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Living in the countryside, my only Internet connection is a weak LTE link. I have 3 NixOS and 1 Guix system at home. Any mass-rebuild takes me at least 2 hours to substitute on a good day.&lt;/p&gt;
&lt;p&gt;Needless to say, I have a personal urge to improve that situation!&lt;/p&gt;
&lt;p&gt;Before blindly stepping into what looks like a multi-year-long sustained implementation effort, I wanted to gather some hard data showing how much we could gain from increasing the substitution granularity. It&amp;rsquo;s benchmarking time.&lt;/p&gt;
&lt;h2 id=&#34;benchmarking-fine-granularity-substitution-mechanisms&#34;&gt;Benchmarking Fine Granularity Substitution Mechanisms&lt;/h2&gt;
&lt;p&gt;For various scenarios, we&amp;rsquo;re going to build a Nix closure for 2 Nixpkgs commits. Then, we&amp;rsquo;ll simulate an update from the first commit to the second one and see how much data we&amp;rsquo;ll need to download from the binary cache.&lt;/p&gt;
&lt;p&gt;For each scenario, we&amp;rsquo;re going to evaluate the 4 following substitution techniques:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Nar substitution&lt;/strong&gt;: This is the substitution model currently used by the NixOS and Guix projects, it&amp;rsquo;ll act as our metrics baseline. It consists in archiving and compressing a full store path. In this benchmark, we&amp;rsquo;ll identify each NAR by its filename derived from the &lt;code&gt;sha256sum&lt;/code&gt; of its content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Casync substitution&lt;/strong&gt;: This is an experimental substitution mechanism implemented via the &lt;a href=&#34;https://github.com/flokli/nix-casync&#34;&gt;nix-casync&lt;/a&gt; project. Starting from a NAR, we decompress it, serialize all the files in a single stream, and chunk it into smaller bits before compressing them again. In this benchmark, we&amp;rsquo;ll identify each casync chunk by its filename, itself already derived from the &lt;code&gt;sha256&lt;/code&gt; sum of its content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File-based substitution&lt;/strong&gt;: This is the substitution method &lt;a href=&#34;https://lists.gnu.org/archive/html/guix-devel/2021-01/msg00079.html&#34;&gt;the Guix&lt;/a&gt; project brainstormed around. Each store gets substituted separately. In this benchmark, we&amp;rsquo;ll identify these files using the &lt;code&gt;sha256&lt;/code&gt; sum of their content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;XZ-Compressed File-based substitution&lt;/strong&gt;: similar to the File-based substitution except each file is individually compressed using the xz compression mechanism (profile 6 extreme).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you&amp;rsquo;re interested in the nitty-gritty details, the source code and the raw data behind these benchmarks live &lt;a href=&#34;https://github.com/PicNoir/nix-guix-substitution-benchmark&#34;&gt;in this git repository&lt;/a&gt;. You can also have a look at the &lt;a href=&#34;https://alternativebit.fr/assets/substitution-benchmark-jupyter-notebook.html&#34;&gt;Jupyter notebook I used to crunch the benchmarks data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note: we&amp;rsquo;re using NixOS/Nixpkgs for all these benchmarks. However, provided Guix currently uses the same substitution mechanism, you can safely assume the same conclusions holds for it as well.&lt;/p&gt;
&lt;h3 id=&#34;mass-rebuild&#34;&gt;Mass Rebuild&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s kill the suspense right away and have a look at the most interesting scenario first. We&amp;rsquo;re going to simulate a NixOS closure update after the &lt;a href=&#34;https://github.com/NixOS/nixpkgs/pull/148396&#34;&gt;staging-next 2021-12-03&lt;/a&gt; merge containing, among other things, a &lt;code&gt;curl&lt;/code&gt; update. As we already established in the &amp;ldquo;&lt;a href=&#34;#substitution-hell&#34;&gt;Substitution Hell&lt;/a&gt;&amp;rdquo; section, a curl bump will propagate through the whole NixOS dependency graph. That&amp;rsquo;s the worst-case scenario in terms of substitution: the mass rebuild.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re going to use a &lt;a href=&#34;https://github.com/PicNoir/nix-guix-substitution-benchmark/blob/314f0e9fed0245161861ea333f737f6d9d28b955/machine.nix&#34;&gt;NixOS machine description&lt;/a&gt; depending on several diverse language stacks (C, Python, BEAM) and simulate a closure update after this mass rebuild.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see how the four substitution techniques perform:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/nix-substitution/mass-rebuild-diagram.svg&#34; alt=&#34;Mass Rebuild Diagram&#34;&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Closure Size (MB)&lt;/th&gt;
          &lt;th&gt;Downloaded Size (MB)&lt;/th&gt;
          &lt;th&gt;Re-used Size (MB)&lt;/th&gt;
          &lt;th&gt;DL Savings Compared to NAR (%)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;NAR&lt;/td&gt;
          &lt;td&gt;386.06&lt;/td&gt;
          &lt;td&gt;372.99&lt;/td&gt;
          &lt;td&gt;13.0702&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Casync&lt;/td&gt;
          &lt;td&gt;608.652&lt;/td&gt;
          &lt;td&gt;192.195&lt;/td&gt;
          &lt;td&gt;416.457&lt;/td&gt;
          &lt;td&gt;48.4719&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;File&lt;/td&gt;
          &lt;td&gt;1652.19&lt;/td&gt;
          &lt;td&gt;705.665&lt;/td&gt;
          &lt;td&gt;973.348&lt;/td&gt;
          &lt;td&gt;-89.1915&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compressed File&lt;/td&gt;
          &lt;td&gt;476.523&lt;/td&gt;
          &lt;td&gt;229.489&lt;/td&gt;
          &lt;td&gt;247.034&lt;/td&gt;
          &lt;td&gt;38.4732&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;First, the good news: increasing the substitution granularity definitively works! The casync-based substitution almost cuts by half the amount of data we need to download for this mass rebuild.&lt;/p&gt;
&lt;p&gt;YAY!&lt;/p&gt;
&lt;p&gt;The xz-compressed file-based substitution sits in the same ballpark, saving us ~40% worth of data to download.&lt;/p&gt;
&lt;p&gt;We can already see two other things from this experiment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Without proper compression, the file-based substitution performs about half as good as the NAR substitution.&lt;/li&gt;
&lt;li&gt;Looking at the closure size of the various substitution methods, we can realize that the finer the substitution granularity gets, the worse the compression algorithms are performing. In other words, the massive gain in terms of re-used data/downloaded data ratio (~75% for casync) does not directly translate into download savings. We probably want to spend some time investigating the state-of-the-art WRT. compression algorithm specialized in small files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Provided these results, I&amp;rsquo;ll omit the uncompressed file substitution-related data for the next benchmarks: it&amp;rsquo;s consistently worse than the compressed counterpart in terms of performance and hurts the readability of the diagrams.&lt;/p&gt;
&lt;h3 id=&#34;channel-jump&#34;&gt;Channel Jump&lt;/h3&gt;
&lt;p&gt;In this scenario, provided the &lt;a href=&#34;https://github.com/NinjaTrappeur/nix-guix-substitution-benchmark/blob/314f0e9fed0245161861ea333f737f6d9d28b955/machine.nix&#34;&gt;same NixOS machine closure&lt;/a&gt; we used in the Mass Rebuild scenario, we&amp;rsquo;ll simulate a jump from the NixOS-21.11 stable channel to NixOS-unstable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/nix-substitution/channel-jump-diagram.svg&#34; alt=&#34;Channel Jump Diagram&#34;&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Closure Size (MB)&lt;/th&gt;
          &lt;th&gt;Downloaded Size (MB)&lt;/th&gt;
          &lt;th&gt;Re-used Size (MB)&lt;/th&gt;
          &lt;th&gt;DL Savings Compared to NAR (%)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;NAR&lt;/td&gt;
          &lt;td&gt;387.458&lt;/td&gt;
          &lt;td&gt;375.167&lt;/td&gt;
          &lt;td&gt;12.2905&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Casync&lt;/td&gt;
          &lt;td&gt;610.886&lt;/td&gt;
          &lt;td&gt;310.541&lt;/td&gt;
          &lt;td&gt;300.345&lt;/td&gt;
          &lt;td&gt;17.2261&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compressed File&lt;/td&gt;
          &lt;td&gt;478.12&lt;/td&gt;
          &lt;td&gt;307.479&lt;/td&gt;
          &lt;td&gt;170.642&lt;/td&gt;
          &lt;td&gt;18.0423&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I did not expect much savings here, but surprisingly enough, we&amp;rsquo;ll shave about 18% worth of downloaded data using the compressed file-based substitution or casync.&lt;/p&gt;
&lt;p&gt;This scenario does not make total sense from a pragmatic point of view as-is: you rarely jump a NixOS machine closure from a stable to unstable NixOS channel. It however does make sense in the context of a dev machine. You often have to jump between different projects having their own dev &lt;code&gt;nix-shell&lt;/code&gt; closures pointing to different Nixpkgs channels.&lt;/p&gt;
&lt;p&gt;The good and unexpected news is: even in that scenario, we can expect some substantial gains in terms of the amount of data we&amp;rsquo;ll need to download!&lt;/p&gt;
&lt;h3 id=&#34;single-derivation-bump&#34;&gt;Single Derivation Bump&lt;/h3&gt;
&lt;p&gt;Now, one may ask: can we expect any savings for a single derivation substitution?&lt;/p&gt;
&lt;p&gt;Ludovic Courtès recently &lt;a href=&#34;https://lists.gnu.org/archive/html/guix-devel/2020-12/msg00258.html&#34;&gt;posted a summary of his findings&lt;/a&gt; on that matter in the Guix mailing list for various binaries containing various amounts of binary data and text.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s replicate this experiment with our benchmark. We&amp;rsquo;re going to simulate a version bump for Firefox, Gimp, Emacs, and OpenMPI. To do so, we&amp;rsquo;ll build the two derivations before and after the version bump, and from there see how much data we&amp;rsquo;ll be able to re-use and how much data we&amp;rsquo;ll need to download.&lt;/p&gt;
&lt;h4 id=&#34;firefox&#34;&gt;Firefox&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/nix-substitution/firefox-bump.svg&#34; alt=&#34;Firefox bump results bar chart diagram. See the table below for a screen reader accessible alternative&#34;&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Closure Size (MB)&lt;/th&gt;
          &lt;th&gt;Downloaded Size (MB)&lt;/th&gt;
          &lt;th&gt;Re-used Size (MB)&lt;/th&gt;
          &lt;th&gt;DL Savings Compared to NAR (%)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;NAR&lt;/td&gt;
          &lt;td&gt;219.352&lt;/td&gt;
          &lt;td&gt;56.4315&lt;/td&gt;
          &lt;td&gt;162.92&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Casync&lt;/td&gt;
          &lt;td&gt;342.925&lt;/td&gt;
          &lt;td&gt;55.871&lt;/td&gt;
          &lt;td&gt;287.054&lt;/td&gt;
          &lt;td&gt;0.993342&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compressed File&lt;/td&gt;
          &lt;td&gt;260.424&lt;/td&gt;
          &lt;td&gt;55.8295&lt;/td&gt;
          &lt;td&gt;204.594&lt;/td&gt;
          &lt;td&gt;1.06682&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;gimp&#34;&gt;Gimp&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/nix-substitution/gimp-bump.svg&#34; alt=&#34;Gimp bump results bar chart diagram. See the table below for a screen reader accessible alternative&#34;&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Closure Size (MB)&lt;/th&gt;
          &lt;th&gt;Downloaded Size (MB)&lt;/th&gt;
          &lt;th&gt;Re-used Size (MB)&lt;/th&gt;
          &lt;th&gt;DL Savings Compared to NAR (%)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;NAR&lt;/td&gt;
          &lt;td&gt;247.506&lt;/td&gt;
          &lt;td&gt;20.2708&lt;/td&gt;
          &lt;td&gt;227.236&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Casync&lt;/td&gt;
          &lt;td&gt;363.372&lt;/td&gt;
          &lt;td&gt;13.0572&lt;/td&gt;
          &lt;td&gt;350.315&lt;/td&gt;
          &lt;td&gt;35.586&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compressed File&lt;/td&gt;
          &lt;td&gt;300.3&lt;/td&gt;
          &lt;td&gt;10.1874&lt;/td&gt;
          &lt;td&gt;290.113&lt;/td&gt;
          &lt;td&gt;49.7434&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;emacs&#34;&gt;Emacs&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/nix-substitution/emacs-bump.svg&#34; alt=&#34;Emacs bump results bar chart diagram. See the table below for a screen reader accessible alternative&#34;&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Closure Size (MB)&lt;/th&gt;
          &lt;th&gt;Downloaded Size (MB)&lt;/th&gt;
          &lt;th&gt;Re-used Size (MB)&lt;/th&gt;
          &lt;th&gt;DL Savings Compared to NAR (%)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;NAR&lt;/td&gt;
          &lt;td&gt;110.331&lt;/td&gt;
          &lt;td&gt;39.7443&lt;/td&gt;
          &lt;td&gt;70.5862&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Casync&lt;/td&gt;
          &lt;td&gt;177.734&lt;/td&gt;
          &lt;td&gt;44.7264&lt;/td&gt;
          &lt;td&gt;133.007&lt;/td&gt;
          &lt;td&gt;-12.5355&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compressed File&lt;/td&gt;
          &lt;td&gt;136.6&lt;/td&gt;
          &lt;td&gt;40.7356&lt;/td&gt;
          &lt;td&gt;95.8641&lt;/td&gt;
          &lt;td&gt;-2.49415&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;open-mpi&#34;&gt;Open MPI&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/nix-substitution/openmpi-bump.svg&#34; alt=&#34;Open MPI bump results bar chart diagram. See the table below for a screen reader accessible alternative&#34;&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Closure Size (MB)&lt;/th&gt;
          &lt;th&gt;Downloaded Size (MB)&lt;/th&gt;
          &lt;th&gt;Re-used Size (MB)&lt;/th&gt;
          &lt;th&gt;DL Savings Compared to NAR (%)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;NAR&lt;/td&gt;
          &lt;td&gt;139.54&lt;/td&gt;
          &lt;td&gt;3.33596&lt;/td&gt;
          &lt;td&gt;136.204&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Casync&lt;/td&gt;
          &lt;td&gt;214.708&lt;/td&gt;
          &lt;td&gt;4.38082&lt;/td&gt;
          &lt;td&gt;210.328&lt;/td&gt;
          &lt;td&gt;-31.3209&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Compressed File&lt;/td&gt;
          &lt;td&gt;167.518&lt;/td&gt;
          &lt;td&gt;3.26096&lt;/td&gt;
          &lt;td&gt;164.257&lt;/td&gt;
          &lt;td&gt;2.24831&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Once again, looking at the closure sizes, we can see we are experiencing a loss in terms of compression efficiency. We have to pay back that loss with an even greater substitution performance to be better or even on par with the NAR-based substitution.&lt;/p&gt;
&lt;p&gt;So, does a increase in substitution granularity helps when substituting a single derivation?&lt;/p&gt;
&lt;p&gt;The answer is: it depends.&lt;/p&gt;
&lt;p&gt;While it&amp;rsquo;s clear that on a NixOS closure level the gains are almost always considerable, on a single derivation substitution, the gains will greatly depend on the content of the derivation. In the case of a derivation mostly containing text files such as Gimp, we can expect lot of savings. In the case of a derivation mostly consisting of binary data (Firefox, Emacs), the gains are likely to be residual.&lt;/p&gt;
&lt;p&gt;Casync is performing consistently worse than the compressed files approach. This can probably be explained by two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Loss of the file boundaries: the chunks are generated from a serialized stream containing the whole store path content. It&amp;rsquo;d probably be a good idea to instead chunk the files individually in our use case.&lt;/li&gt;
&lt;li&gt;Shy compression: the Casync closure size is almost twice (!!) as big as the NAR one. While the small size of the chunks can explain to some extent this compression efficiency loss, &lt;code&gt;nix-casync&lt;/code&gt; is also using lzma to compress its chunks. Moving to more aggressive xz compression could be a good idea here.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;long-road-ahead&#34;&gt;Long Road Ahead&lt;/h2&gt;
&lt;p&gt;So, what&amp;rsquo;s the take-away here?&lt;/p&gt;
&lt;p&gt;Well, to me, the main one is that the current Nix/Guix substitution bloat story is not helpless. We can expect some serious improvements by increasing the substitution granularity. Be it using a file-based substitution mechanism or using a chunk-based one.&lt;/p&gt;
&lt;p&gt;It would probably be worth investing some resources into pushing these substitution mechanisms to a production-ready state. We should also seriously consider using them as the default substitution mechanism once they&amp;rsquo;re ready. And potentially give up on the NAR-based approach.&lt;/p&gt;
&lt;p&gt;Increasing the substitution granularity won&amp;rsquo;t sadly come for free in terms of complexity. It&amp;rsquo;ll affect a lot of things, possibly including rethinking the binary cache signature mechanism. We&amp;rsquo;ll likely need quite some time before being able to confidently replace the NAR-based substitution mechanism. It&amp;rsquo;s definitely not a weekend project.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Special thanks to &lt;a href=&#34;https://profpatsch.de&#34;&gt;Profpatsch&lt;/a&gt; for his spot on review.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Full disclosure, while it&amp;rsquo;s okay to omit them in the context of this article, there are two notable exceptions to this input-addressed nature:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fixed Output Derivations (FODs): they are, as their name implies, already output-addressed. We know, at build time, their content hash. We use them to deterministically download stable artifacts from the Internet. EG. bootstrap seed, source code, binaries…&lt;/li&gt;
&lt;li&gt;A content-addressed store mode is available through an experimental feature. See &lt;a href=&#34;https://github.com/NixOS/rfcs/blob/master/rfcs/0062-content-addressed-paths.md&#34;&gt;RFC#62&lt;/a&gt; for more informations.&lt;/li&gt;
&lt;/ul&gt;
&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Bias Warning: the main author of nix-casync is an online friend.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>Ex-Hack: a Haskell Example-based Documentation</title>
      <link>https://alternativebit.fr/posts/haskell/ex-hack-alpha/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Thu, 15 Nov 2018 13:49:00 +0100</pubDate>
      <guid>https://alternativebit.fr/posts/haskell/ex-hack-alpha/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ex-hack/ex-hack-full.svg&#34; alt=&#34;Ex-Hack logo: a low-fi portrait of Jamy
Gourmaud&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;abstract&#34;&gt;Abstract&lt;/h2&gt;
&lt;p&gt;Ex-Hack is an example-based documentation automatically generated using the
packages posted on Stackage. There&amp;rsquo;s a &lt;a href=&#34;https://exhack.org&#34;&gt;live demo here&lt;/a&gt;.
We&amp;rsquo;ve just released the alpha version; you can have a look to the &lt;a href=&#34;https://github.com/PicNoir/ex-hack&#34;&gt;code
here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are actively looking for
&lt;a href=&#34;https://github.com/PicNoir/ex-hack/labels/Todo&#34;&gt;new contributors&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After briefly introducing the project&amp;rsquo;s incentives and explaining how this
software works, we discuss the current roadmap and what we need to do before
releasing the V1.0.0.&lt;/p&gt;
&lt;h2 id=&#34;an-effort-to-fill-the-documentation-gap&#34;&gt;An Effort to Fill the Documentation Gap&lt;/h2&gt;
&lt;p&gt;Accordingly to the &lt;a href=&#34;http://taylor.fausak.me/2017/11/15/2017-state-of-haskell-survey-results/&#34;&gt;2017 Haskell survey results&lt;/a&gt;,
the lack of good documentation seems to be a major issue in the Haskell
community.&lt;/p&gt;
&lt;p&gt;How can we fix this? The obvious solution is to write more documentation.
However, writing more documentation does not necessarily mean writing a better
one. The best documentation writers I met are not necessarily the best
programmers I met. Being able to write a clear, concise and useful
documentation is a very specific skill that takes time to acquire.&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s one thing you can add to your documentation requiring very
little writing skill and still being extremely useful: examples. We, as humans,
tend to often learn by mimicking other&amp;rsquo;s behaviour. Adding examples to a
documentation is in my experience a local maximum: it&amp;rsquo;s not really hard to do,
yet it dramatically increases your documentation&amp;rsquo;s quality.&lt;/p&gt;
&lt;p&gt;This is where I started thinking to automatically generate these examples. The
Haskell community is fortunate enough to have a centralized code
repository: Hackage. The packages stored in this repository are usually getting
their dependencies from this very same repository. Maybe we could use all this
code to extract some real-world examples.&lt;/p&gt;
&lt;p&gt;It would be great if on top of the current type documentation we could add an
automatically generated example section in Haddock.&lt;/p&gt;
&lt;p&gt;This was the starting point of the whole ex-hack project: generate an
example-based documentation we could later use in Haddock.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get this straight right away: we still have a lot of work to do before
merging this project to Haddock, but we made some serious progress in the last
5 months!&lt;/p&gt;
&lt;h2 id=&#34;so-how-does-it-work&#34;&gt;So, How does it Work?&lt;/h2&gt;
&lt;p&gt;What do we need to generate such a database?&lt;/p&gt;
&lt;p&gt;Well, basically, for each Stackage/Hackage package, we need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download it.&lt;/li&gt;
&lt;li&gt;Build it.&lt;/li&gt;
&lt;li&gt;Extract the symbols exported by its main library.&lt;/li&gt;
&lt;li&gt;Index the symbols coming other packages.&lt;/li&gt;
&lt;li&gt;Generate a static HTML documentation displaying the previously extracted
informations.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The whole project is centered around the &lt;code&gt;processing step&lt;/code&gt; abstraction. Each
processing step is dependent from the previous one and is a dependency for the
next one.&lt;/p&gt;
&lt;p&gt;This means that ideally, we could run this software in a map/reduce
configuration and distribute the load over several nodes. This comes handy,
because as stated previously: we&amp;rsquo;ll need to build the whole Stackage/Hackage.
Needless to say, this is a seriously time-consuming process. The more we can
distribute the load, the faster we&amp;rsquo;ll generate the documentation.&lt;/p&gt;
&lt;p&gt;So far, we&amp;rsquo;ve been able to generate the documentation for Stackage (~2,400
packages). The last run (which populated the current
&lt;a href=&#34;https://exhack.org&#34;&gt;demo&lt;/a&gt;) took ~25 hour to complete on my 7 YO desktop. If
we want to scale this project to the whole Hackage (~13 000 packages)
repository, we&amp;rsquo;ll probably need to distribute this generation on several nodes.&lt;/p&gt;
&lt;h2 id=&#34;projects-roadmap&#34;&gt;Project&amp;rsquo;s Roadmap&lt;/h2&gt;
&lt;p&gt;Which bring us to the million dollar question: what&amp;rsquo;s next?&lt;/p&gt;
&lt;p&gt;Before entering in the beta phase, we first need to address several issues
making ex-hack documentation &lt;strong&gt;not fully usable&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, we need to &lt;a href=&#34;https://github.com/PicNoir/ex-hack/issues/4&#34;&gt;re-write the symbol occurrence indexing
system&lt;/a&gt;. The current
approach clearly does not work.&lt;/li&gt;
&lt;li&gt;We need to &lt;a href=&#34;https://github.com/PicNoir/ex-hack/issues/7&#34;&gt;setup a proper
environment&lt;/a&gt; to execute
the whole database generation. Some packages need some system dependencies
(such as libgl, zlib, etc.) to be built. We are currently unable to build
them. Hence, we are indexing 2082 packages instead of the ~2,400 contained in
Stackage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, it&amp;rsquo;ll be time to address some usability issues before releasing the first
stable version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writing a second UI targeted to package maintainers; showing them what are the
most used parts of their API. It could come handy when performing a major API
refactoring ;)&lt;/li&gt;
&lt;li&gt;Writing a &lt;a href=&#34;https://github.com/PicNoir/ex-hack/issues/11&#34;&gt;Haskell IDE Engine&lt;/a&gt; plugin.&lt;/li&gt;
&lt;li&gt;Partially re-write the current HTML documentation to make it more pleasant to
use.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;ultimate-goal&#34;&gt;Ultimate Goal&lt;/h3&gt;
&lt;p&gt;Once ex-hack will reach its definitive form, I would love to see this merged
back to Haddock. I think having usage examples next to the type and author&amp;rsquo;s
comments for each library symbol would make a great documentation.&lt;/p&gt;
&lt;p&gt;However, entirely merging this to Haddock is going to be challenging. First, we
would need a new Haddock component in charge to build the ex-hack documentation
for every new package/stackage release.&lt;/p&gt;
&lt;p&gt;We would also need a way to retrieve an already populated ex-hack database
before generating a Haddock documentation for a library: you can&amp;rsquo;t expect to
build the whole Hackage/Stackage on a developer&amp;rsquo;s machine!&lt;/p&gt;
&lt;p&gt;Anyways, looks challenging, but you always need a long-term goal dream right?
:D&lt;/p&gt;
&lt;h2 id=&#34;how-to-join-us&#34;&gt;How to Join Us?&lt;/h2&gt;
&lt;p&gt;So far, I&amp;rsquo;ve been mostly working on this in isolation; which was arguably not
my best move. The more people will work on this, the more progress we&amp;rsquo;ll make.&lt;/p&gt;
&lt;p&gt;Most of the annoying early project exploration has already been made, we
mostly know were we are heading now. We mostly need to &amp;ldquo;industrialize&amp;rdquo; the
project. You don&amp;rsquo;t need to be a Haskell guru to contribute, as a matter of
fact I&amp;rsquo;m far from being one.&lt;/p&gt;
&lt;p&gt;For now, the GitHub repository will act as the main community hub. Pick an
issue — some are labeled as
&lt;a href=&#34;https://github.com/PicNoir/ex-hack/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22&#34;&gt;newcomer-friendly&lt;/a&gt;
— and just start hacking on it :)&lt;/p&gt;
&lt;p&gt;If you have some questions/suggestions or just want to say hi, there&amp;rsquo;s also a
more informal communication channel: the
&lt;a href=&#34;http://webchat.freenode.net?channels=%23ex-hack&#34;&gt;#ex-hack&lt;/a&gt; room on the
Freenode IRC network.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Ultimate Writer: an Open Digital Typewriter</title>
      <link>https://alternativebit.fr/posts/ultimate-writer/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/hardware">Hardware</category>
      <pubDate>Wed, 17 Oct 2018 09:37:05 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/ultimate-writer/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; A digital typewriter based on a Raspberry Pi and an E-Ink screen.
&lt;a href=&#34;https://github.com/PicNoir/ultimate-writer&#34;&gt;The code/build instructions are available on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/intro-image.jpg&#34; alt=&#34;Mysterious, yet a little bit revealing image of the ultimate writer&#34;&gt;&lt;/p&gt;
&lt;p&gt;I am easily distracted.&lt;/p&gt;
&lt;p&gt;This is both a blessing and a curse. On one hand, I can deal with a large
amount of boredom without driving crazy: remaining artifact from my school
years and necessary skill to survive in our meeting-based modern corporate
environment. On the other hand, completing a task requiring more than two
minutes of my attention can turn into an escape game.&lt;/p&gt;
&lt;p&gt;My behavior is far from being exceptional. As a matter of fact, some dodgy
people are making entire careers selling people like me books with clickbaity
titles.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Overcome your Procrastination and Setup your own Ponzi Scheme in 30 Days.&lt;/p&gt;
&lt;p&gt;The definitive guide.&lt;/p&gt;
&lt;p&gt;- PicNoir 2018&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;More seriously, I am a firm believer that restriction fuels creativity. I don&amp;rsquo;t
think it&amp;rsquo;s the result of a transcendental zen achievement, this is more about
restricting the possible creative outcomes. The less you have to explore what
you can create, the more headspace you have to actually create things.&lt;/p&gt;
&lt;p&gt;This is why I love my typewriter. You can write stories without having to wait
for your computer to boot, you cannot be distracted by some kind of
notification or the urge to check your favorite online place every half an
hour.&lt;/p&gt;
&lt;p&gt;However, using a typewriter comes with two major shortcomings:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You cannot delete a typo. I know you do a lot of those, no need to expose
this nasty behaviour to everybody&amp;rsquo;s face.&lt;/li&gt;
&lt;li&gt;You cannot share the text you wrote online, be it on your website or on any
other social platform.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I came to think, maybe we should create a digital typewriter giving us the best
of the two worlds.&lt;/p&gt;
&lt;p&gt;Digital typewriters are not a new idea. In fact, one of them has even been
commercialized: the &lt;a href=&#34;https://getfreewrite.com/&#34;&gt;Freewrite&lt;/a&gt;.
To be honest, this project has been a major inspiration for the ultimate
writer. It looks cool, has a mechanical keyboard and an e-ink screen. Sadly,
their firmware is completely closed (ie. no way to use vim on it), and the
price tag is &lt;strong&gt;pretty steep&lt;/strong&gt;, to say the least.&lt;/p&gt;
&lt;p&gt;After a quick search on the internet, I was unable to find an open hardware
equivalent. I decided to create one: the ultimate writer.&lt;/p&gt;
&lt;h2 id=&#34;the-first-prototype&#34;&gt;The First Prototype&lt;/h2&gt;
&lt;p&gt;This project was built upon the following principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easily readable e-ink screen&lt;/strong&gt;. You can read it effortlessly even in
sunlight.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Long lasting battery life&lt;/strong&gt;. You can have a 3 days off the grid writing
retreat (~20 hours) without having to recharge it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easily serviceable design&lt;/strong&gt;. Your typewriter is 40 years old and works just
fine. You don’t want to change your writing device every 5 years. You want to
be able to easily change the computer parts easily; and who knows, use
something else than a raspberry pi.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standard OS&lt;/strong&gt;. You want to use your favorite console-based text editor. You
also want a shell access to tweak your setup without reprogramming the
device.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nice full-size mechanical keyboard&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first prototype covers most of these features.&lt;/p&gt;
&lt;p&gt;Overall, it looks like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/general-view.jpg&#34; alt=&#34;Picture of the Ultimate Writer&#34;&gt;&lt;/p&gt;
&lt;p&gt;It cost me around 150€ (~180$) to build.&lt;/p&gt;
&lt;p&gt;The mechanical keyboard is a 61 keys Chinese bootleg. It feels pretty good
despite its really low price.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/keyboard.jpg&#34; alt=&#34;Picture of the Keyboard&#34;&gt;&lt;/p&gt;
&lt;p&gt;The E-Ink screen is coming from good-display. They don&amp;rsquo;t seem to sell them
directly, but you can buy them from waveshare, a Shenzhen-based middle-man.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/screen2.jpg&#34; alt=&#34;Picture of the E-Ink Screen&#34;&gt;&lt;/p&gt;
&lt;p&gt;The case has been made from a locally grown cypress. I did not use any varnish,
this awesome pink-ish color is the natural one. I love this wood &amp;lt;3.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/closed-case.jpg&#34; alt=&#34;Picture of the Case (closed)&#34;&gt;&lt;/p&gt;
&lt;p&gt;The computer is running a standard Raspbian, I mostly use neovim to edit text.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/shell.jpg&#34; alt=&#34;Picture of the Computer Displaying a Debian Shell&#34;&gt;&lt;/p&gt;
&lt;p&gt;The battery life turned out to be more around 16 hours. However, the Raspberry
Pi 3B is not known for being energy-savvy. I guess we could easily reach the 20
hours autonomy by using a smaller computer.&lt;/p&gt;
&lt;h2 id=&#34;build-log&#34;&gt;Build Log&lt;/h2&gt;
&lt;p&gt;Nerdy stuff ahead. If you&amp;rsquo;re not interested in the technical details, you can
directly jump to the &lt;a href=&#34;#what-s-next&#34;&gt;what&amp;rsquo;s next section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I started the whole prototype build with an assertion in mind: I need to reach
a usable state for this project as quick as possible. I actually abandoned
this very same project halfway through twice in the past.&lt;/p&gt;
&lt;p&gt;In other terms, when I encountered a roadblock - and there have been many - I
privileged the quick workaround to the labor expensive clean solution.&lt;/p&gt;
&lt;h3 id=&#34;e-ink-screen&#34;&gt;E-Ink Screen&lt;/h3&gt;
&lt;p&gt;First of all, we need to find a suitable E-Ink screen. Despite being old, this
technology is still a niche. Finding a manufacturer able to create large
(&amp;gt; 4&amp;quot;) screens turned out to be tricky.&lt;/p&gt;
&lt;p&gt;Spare E-Readers screens are the best lead: they are crazy cheap, decently sized
and widely available. However, there is no off the shelf solution to drive
them.&lt;/p&gt;
&lt;p&gt;We desperately need an open source driver for those screens, I considered
creating one. But as mentioned earlier: I want to build a usable prototype as
quickly as possible, I don&amp;rsquo;t wanna be side-tracked.&lt;/p&gt;
&lt;p&gt;Being unable to use a spare e-reader screen, I went the other way around and
started looking for a manufacturer selling a screen which is directly usable
by a stock Raspberry PI.&lt;/p&gt;
&lt;p&gt;In the end, only one screen manufacturer seemed to be delivering that kind of
screen: &lt;a href=&#34;http://www.good-display.com/products_list/pmcId=29.html&#34;&gt;good display&lt;/a&gt;. These screens - like
a lot of Chinese products - are rebranded all over the internet. Sometimes,
they are re-branded and have half their datasheet butchered in the process -
yes, I am looking at you Waveshare.&lt;/p&gt;
&lt;p&gt;These screens are coming in &lt;a href=&#34;https://alternativebit.fr/images/ultimate-writer/screen.jpg&#34;&gt;three parts&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Raspberry Pi hat.&lt;/li&gt;
&lt;li&gt;A small IC connecting the hat to the screen.&lt;/li&gt;
&lt;li&gt;The screen by itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite how it looks, both of the blue CIs are breakout boards. The driver is
embedded in the screen and is controlled by a standard &lt;a href=&#34;https://fr.wikipedia.org/wiki/Serial_Peripheral_Interface&#34;&gt;SPI&lt;/a&gt; interface.&lt;/p&gt;
&lt;p&gt;These screens are easy to use. Sadly, they have very a slow refresh rate: it
takes around 3 seconds to perform a redraw. Needless to say, it&amp;rsquo;s way too
slow to display any kind of interactive program.&lt;/p&gt;
&lt;p&gt;These screens are driven using electromagnetic waveforms, Ben Kraznow from the
Applied Science youtube channel made a
&lt;a href=&#34;https://www.youtube.com/watch?v=MsbiO8EAsGw&#34;&gt;fantastic video&lt;/a&gt; about this. You
really should check it out, at least the first half.&lt;/p&gt;
&lt;p&gt;Very quick and incomplete summary: in order to refresh the screen, you apply a
specific waveform to each pixel depending on its previous and future state.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s great about good display screens is that you have a direct access to the
registers specifying these waveforms. You can entirely reprogram the way the
screen is driven.&lt;/p&gt;
&lt;p&gt;Ben Kraznow found a way to implement a faster partial refresh which let you
refresh the screen in about 0.3 seconds, which is more than enough for a text
editing device. He implemented this for the 4.3&amp;quot; screen, I sadly haven&amp;rsquo;t been
able to implement the same kind of fast refresh for the 7.5&amp;quot; screen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I&amp;rsquo;m going to dive in the nasty implementations details here and
explain why we can&amp;rsquo;t have a fast refresh on the 7.5&amp;quot; screen for now. You may
want to jump to the &lt;a href=&#34;#terminal-emulation&#34;&gt;next section&lt;/a&gt; if you&amp;rsquo;re not
interested.&lt;/p&gt;
&lt;p&gt;Under the hood, these waveforms are specified using several look-up tables
(LUTs). You have 4 of them: &lt;code&gt;black-&amp;gt;black&lt;/code&gt;, &lt;code&gt;white-&amp;gt;white&lt;/code&gt;, &lt;code&gt;black-&amp;gt;white&lt;/code&gt; and
&lt;code&gt;white-&amp;gt;black&lt;/code&gt;. These LUTs are specific to each screen design.&lt;/p&gt;
&lt;p&gt;You have two ways to specify these LUTs to the &lt;a href=&#34;https://alternativebit.fr/data/ultimate-writer/IL0371.pdf&#34;&gt;screen
controller&lt;/a&gt; (take care, waveshare
data sheets are
incomplete, use the good display ones previously linked instead).&lt;/p&gt;
&lt;p&gt;You can either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the pre-loaded LUTs in the IL0371 read-only flash.&lt;/li&gt;
&lt;li&gt;Use the LUTs stored in the IL0371 register which can be accessed using the SPI
interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Good display stock firmware uses the second method to specify the LUTs for the
smaller screens (&amp;lt; 7.5&amp;quot;). This is kind of useful when it comes to write a
partial refresh mode because it means the LUTs are specified directly in
raspberry pi library source code. We just have to extract them from there.&lt;/p&gt;
&lt;p&gt;Ben Kraznow basically took these full refresh LUTs and removed all the
intermediate drawing steps to only keep the last one. This solution works
pretty great, you just still need to perform a full refresh periodically to
correctly re-magnetize the burned greyish pixels.&lt;/p&gt;
&lt;p&gt;I was first planning to do the same thing for the 7.5&amp;quot; display. Sadly, for some
reason, good display is pre-loading the LUTs in the read-only flash for this
model. We can&amp;rsquo;t extract the full refresh LUT from the driver firmware.&lt;/p&gt;
&lt;p&gt;This leaves us with two solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We manage to dump the internal screen flash memory. This should be doable
using the ROTP command. Sadly, I haven&amp;rsquo;t been able to read anything from my
screen. I cannot tell if the problems come from a failure from my specific
hardware setup or if somebody in the supply chain (waveshare? good display?)
has been somehow disabling the MISO channel.&lt;/li&gt;
&lt;li&gt;We ask a manufacturer for the LUTs. I have been in contact with good display
engineers, but they have been unable to give me access to either the LUTs or
the data flashed in the IL0371.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I tried both of these solutions, neither worked. This has been the main
roadblock for this project. So far, I just use the &amp;ldquo;slow refresh&amp;rdquo; and deal with
the .3Hz refresh rate.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;m not gonna lie, while it&amp;rsquo;s enough to write text, it&amp;rsquo;s not remotely fast
enough to edit the same text. Using vim interactively is not as nice as on my
laptop&amp;rsquo;s 60Hz LCD screen.&lt;/p&gt;
&lt;h3 id=&#34;terminal-emulation&#34;&gt;Terminal Emulation&lt;/h3&gt;
&lt;p&gt;Alright, we can draw some pixels on the screen, let&amp;rsquo;s display something more
useful: a terminal.&lt;/p&gt;
&lt;p&gt;Have you ever taken a deep dive in terminal emulation? Did you manage to keep
your sanity during the process? Fun fact: I did not.&lt;/p&gt;
&lt;p&gt;Terminal emulation is a pile of legacy layers stacked during the last 40 years.
Despite knowing that, if you&amp;rsquo;re still interested by the details, &lt;a href=&#34;http://www.linusakesson.net/programming/tty/index.php&#34;&gt;Linus Åkesson&lt;/a&gt; - yes, the
very same guy who created the &lt;a href=&#34;https://www.youtube.com/watch?v=MWs0eSIym6Y&#34;&gt;Chipophone&lt;/a&gt;, what a small world! - wrote a
pretty good article on this matter.&lt;/p&gt;
&lt;p&gt;Writing a full terminal emulator is actually a lot of work! So instead
of starting a new one from scratch, I decided to adapt an already existing one.&lt;/p&gt;
&lt;p&gt;When thinking about terminal emulators, XTerm comes first in mind. I started to
dig in its code to be sure the adaptation wouldn&amp;rsquo;t turn into an unpleasant
experience.&lt;/p&gt;
&lt;p&gt;Oh boy, if you ever wondered how painful can legacy code be, please, check out
&lt;a href=&#34;http://invisible-island.net/datafiles/release/xterm.tar.gz&#34;&gt;XTerm&lt;/a&gt;: it is a
masterpiece in its own kind.&lt;/p&gt;
&lt;p&gt;Actually, I could have been smart and read the readme file instead:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-none&#34; data-lang=&#34;none&#34;&gt;Abandon All Hope, Ye Who Enter Here


This is undoubtedly the most ugly program in the distribution.  It was one of
the first &amp;#34;serious&amp;#34; programs ported, and still has a lot of historical baggage.
Ideally, there would be a general tty widget and then vt102 and tek4014
subwidgets so that they could be used in other programs.  We are trying to
clean things up as we go, but there is still a lot of work to do.

If you are porting this to a machine that has problems with overlapping
bcopy&amp;#39;s, watch out!

There are two documents on xterm: the man page, xterm.man, which describes
how to use it, and ctlseqs.ms, which describes the control sequences it
understands.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What do we learn here? Always read the README file first.&lt;/p&gt;
&lt;p&gt;Anyways, after looking a bit for something else, I found out the Suckless team
created a terminal emulator: &lt;a href=&#34;https://st.suckless.org/&#34;&gt;ST&lt;/a&gt;. I decided to use
it.&lt;/p&gt;
&lt;p&gt;I started by chopping out the XOrg related features, I did not want an
unnecessary dependency on X libs. After chopping everything out, I started to
replace these calls with the E-Ink drawing primitives.&lt;/p&gt;
&lt;p&gt;The e-ink screen stock library was written in C++: it needed a full C rewrite
to function properly along with the ST codebase. I found and fixed several bugs
in the process, most of them were due to poor error handling.&lt;/p&gt;
&lt;h3 id=&#34;input-processing&#34;&gt;Input Processing&lt;/h3&gt;
&lt;p&gt;Keyboard inputs, how do we capture them? Well, there&amp;rsquo;s no immediate answer to
that.&lt;/p&gt;
&lt;p&gt;See,  we don&amp;rsquo;t have any graphic stack redirecting the keyboard inputs to stdin.
The keyboard events are handled by linux&amp;rsquo;s /dev/inputX virtual devices. You may
think &amp;ldquo;well, just pipe this file in and you&amp;rsquo;re good&amp;rdquo;. Well, no. This file is
using a custom event-based binary format. In it, you&amp;rsquo;ll find the various key
press, key releases, and other events, but you&amp;rsquo;ll need to process them
accordingly to a global state - is shift pressed? Is ctrl pressed? - and
translate these events to their characters and  pipe everything to stdin.&lt;/p&gt;
&lt;p&gt;The best solution I ended with was to use a keylogger and &lt;code&gt;tail -f&lt;/code&gt; the file to
the application stdin. It could be certainly better, but it is good enough for
now.&lt;/p&gt;
&lt;p&gt;Not really tricky but worth mentioning: there isn&amp;rsquo;t any arrow key nor ctrl
ASCII value. These are actually translated to VT100 special sequences. Keep in
mind you need to translate these specials keys to their
&lt;a href=&#34;http://wiki.bash-hackers.org/scripting/terminalcodes&#34;&gt;associated sequence&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;the-case&#34;&gt;The Case&lt;/h3&gt;
&lt;p&gt;Gotta admit I have been lucky here. I&amp;rsquo;ve been offered some locally-grown dry
cypress to build it. Thanks dad! This wood is amazing: it looks nice, it&amp;rsquo;s
crazily light and really tender. Working with it has been such a pleasure!&lt;/p&gt;
&lt;p&gt;Having access to a whole woodworking workshop also made that build really easy!&lt;/p&gt;
&lt;p&gt;I wanted to use &lt;a href=&#34;https://en.wikipedia.org/wiki/Dovetail_joint&#34;&gt;dovetail joints&lt;/a&gt; to seal the case. Sadly, I ran
out of time and had one day to complete both the design and the build of the
casing; way too little time to perform 8 dovetail joints.&lt;/p&gt;
&lt;p&gt;We used a miter saw to cut the 45° joints. I usually use a good old handsaw for
this kind of work, needless to say, the miter saw is a total game changer!&lt;/p&gt;
&lt;p&gt;I found the hinges in my local hardware store. The compas were more tricky to
find due to their small size; I managed to find mines on eBay.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/compas-hinge.jpg&#34; alt=&#34;Detailed view of the hinges and compas&#34;&gt;&lt;/p&gt;
&lt;p&gt;The case could clearly be better. But for a first iteration, it&amp;rsquo;s more than ok.&lt;/p&gt;
&lt;p&gt;We could do better on an ergonomics standpoint though. Maybe by adding a
foldable wrist rest? As it is, the keyboard is a bit too high and tends to put
some tension on your wrists making typing a bit uncomfortable on the long run.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/wirst-keyboard.jpg&#34; alt=&#34;Picture Showing the Missing Palmrest on the Edge of the keyboard&#34;&gt;&lt;/p&gt;
&lt;p&gt;Anyways, overall, I am satisfied with this design.&lt;/p&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;
&lt;p&gt;I used a Raspeberry Pi 3B for this prototype mostly because I already owned
one. While having an integrated WIFI module has been quite handy, the power
consumption of the computer could be lower. I don&amp;rsquo;t think we need something &amp;ldquo;as
powerful&amp;rdquo; as this for a simple typewriter. For the next iteration, I think we
should evaluate some other small boards and choose something more appropriate.&lt;/p&gt;
&lt;p&gt;I am using regularly this typewriter. As a matter of fact, I am typing this
article on it right now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/ultimate-writer/right-now.jpg&#34; alt=&#34;Picture of the Prototype Having this Article Draft on the Screen&#34;&gt;&lt;/p&gt;
&lt;p&gt;However, I find the current screen refresh rate way too low. Don&amp;rsquo;t get me
wrong, it is &lt;strong&gt;usable&lt;/strong&gt;, but I feel like the latency makes the whole experience
not as pleasant as it could be.&lt;/p&gt;
&lt;p&gt;My middle-term objective regarding this project is to produce a small batch of
those with a more compact wooden case and sell them. Sadly, because of the low
screen refresh rate, I don&amp;rsquo;t think we&amp;rsquo;re at this point yet.&lt;/p&gt;
&lt;p&gt;I need your help to finish this project. How could you help me? Well, several
ways to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are you one of these hardware geniuses? Do you think you might be able to
create a board that can drive a spare kindle screen at a &amp;gt;2Hz rate? Would you
be ok to put your design under an open hardware license? Please get in touch
with me and let&amp;rsquo;s finish this open hardware/FOSS digital typewriter together!
You can find my contact details on the &lt;a href=&#34;https://alternativebit.fr/&#34;&gt;about&lt;/a&gt; page.&lt;/li&gt;
&lt;li&gt;Do you know such a person? Please, forward them this article :)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Otherwise, you still can boost &lt;a href=&#34;https://social.alternativebit.fr/notice/360338&#34;&gt;this fediverse post&lt;/a&gt; or &lt;a href=&#34;https://twitter.com/picnoir_/status/1052465280641851392&#34;&gt;this tweet&lt;/a&gt; to help me reach
this kind of people.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Special thanks to Malix for the nice pictures!&lt;/em&gt; &amp;lt;3&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Loading a Cabal module in the GHC API</title>
      <link>https://alternativebit.fr/posts/haskell/cabal-ghc-api/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Wed, 08 Aug 2018 12:01:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/haskell/cabal-ghc-api/</guid>
      <description>&lt;p&gt;If you plan to build some Haskell tooling or any kind of static code analyzer,
chances are you&amp;rsquo;ll need to use the GHC API at some point.&lt;/p&gt;
&lt;p&gt;While loading a simple module into GHC&amp;rsquo;s API is quite trivial and well
documented, loading complex modules (modules having some c dependencies, some specific
options in the .cabal file, etc.) will require you to find the appropriate
&lt;a href=&#34;https://hackage.haskell.org/package/ghc-8.4.3/docs/GHC.html#t:DynFlags&#34;&gt;dynamic flags&lt;/a&gt;.
These flags are usually retrieved and loaded into GHC by Cabal. Sadly for
us, &lt;a href=&#34;https://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/&#34;&gt;Cabal&amp;rsquo;s API&lt;/a&gt;
does not seems to expose a direct way to get these flags.&lt;/p&gt;
&lt;p&gt;Some people have developed solutions to work around this problem. In this
post, we&amp;rsquo;ll explore two of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Revision:&lt;/strong&gt; If you plan to use GHC &amp;gt;= 8.6.1, you might as well want to check out
GHC source plugins.
&lt;a href=&#34;https://mpickering.github.io/posts/2018-08-09-source-plugin-graphmod.html&#34;&gt;Mpickering&lt;/a&gt;
wrote about them. They will not be mentionned in this article.&lt;/p&gt;
&lt;h2 id=&#34;ghc-plugin--ghci-wrapper&#34;&gt;GHC Plugin + &amp;ldquo;GHCi Wrapper&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;This technique has been detailed by Edward Yang on his &lt;a href=&#34;http://blog.ezyang.com/2017/02/how-to-integrate-ghc-api-programs-with-cabal/&#34;&gt;website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea basically boils down to this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A more reliable way to integrate a GHC API program with Cabal is inversion of
control: have Cabal call your GHC API program, not the other way around!&lt;/p&gt;
&lt;p&gt;[..]&lt;/p&gt;
&lt;p&gt;What we will do is replace the GHC executable which passes through all
commands to an ordinary GHC, except for ghc &amp;ndash;interactive, which we will then
pass to the GHC API program. Then, we will call Cabal repl/stack repl with
our overloaded GHC, and where we would have opened a GHCi prompt, instead our
API program gets run.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You&amp;rsquo;ll first need to write your GHC API program as a GHC frontend plugin. Then, the
dependencies will be built by Cabal using a regular GHC while your
package will be loaded with your custom frontend plugin. You can then
use GHC&amp;rsquo;s API without having to think about any dynamic flags: they already
have been loaded for you by Cabal.&lt;/p&gt;
&lt;p&gt;Pretty smart!&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll still need to get the data extracted by your GHC API program home.
Sadly, your GHC frontend plugin will be instantiated in another process, you
won&amp;rsquo;t have a direct access to it. I guess you could use one of the usual
suspects (stdout, a message queue, a DBMS, etc.) to bring everything home. I
guess you&amp;rsquo;ll also need a proper way to retrieve and manage the errors.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not going to dive more into details here, the [previously linked article]
(&lt;a href=&#34;http://blog.ezyang.com/2017/02/how-to-integrate-ghc-api-programs-with-cabal/&#34;&gt;http://blog.ezyang.com/2017/02/how-to-integrate-ghc-api-programs-with-cabal/&lt;/a&gt;)
contains some code examples, it should more than enough if you want to learn more
about this trick.&lt;/p&gt;
&lt;h2 id=&#34;cabal-helper&#34;&gt;Cabal Helper&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://hackage.haskell.org/package/cabal-helper-0.8.1.0&#34;&gt;Cabal helper&lt;/a&gt; is a
library initially created for ghc-mod.&lt;/p&gt;
&lt;p&gt;The idea here is a bit different, instead of having our GHC API program loaded
by Cabal, we&amp;rsquo;re going to retrieve some build meta-informations left by
Cabal during the package build process and parse them.&lt;/p&gt;
&lt;p&gt;Here, you won&amp;rsquo;t need any GHC wrapper nor frontend plugin.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see how we can practically use this library and the GHC API to retrieve
a module exports.&lt;/p&gt;
&lt;h3 id=&#34;using-cabal-helper-retrieving-a-module-exports&#34;&gt;Using Cabal-helper: retrieving a module exports&lt;/h3&gt;
&lt;p&gt;First, we&amp;rsquo;ll need to let Cabal both install the package&amp;rsquo;s dependencies as
well as building the project.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cabal install --dependencies-only
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cabal build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cabal should have built the necessary artefacts for us to retrieve the
dynamic flags. Let&amp;rsquo;s parse these artifacts and get the options
passed to GHC.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-haskell&#34; data-lang=&#34;haskell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fp&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;path to the dir containing the .cabal file&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;qe&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mkQueryEnv&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;/&amp;gt;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;dist&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;cs&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;runQuery&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;qe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;components&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(,)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ghcOptions&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Your package may contain several Cabal components: a library, an application, a
unit test suite, an integration test suite, etc. Each component is built
differently, they&amp;rsquo;ll each have a different set of dynamic flags.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll probably want to filter this list to extract the component you&amp;rsquo;re
interested in. In this example, let&amp;rsquo;s say we are interested by the lib:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-haskell&#34; data-lang=&#34;haskell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getLib&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;chLibName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getLib&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getLib&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cs&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Altogether, you should have something like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-haskell&#34; data-lang=&#34;haskell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getCabalDynFlagsLib&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;MonadIO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;FilePath&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getCabalDynFlagsLib&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fp&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;qe&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mkQueryEnv&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;/&amp;gt;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;dist&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;cs&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;liftIO&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runQuery&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;qe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;components&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(,)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ghcOptions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;pure&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getLib&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;getLib&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;H&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;ChLibName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;getLib&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;False&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, vaguely. You probably want to reflect some error cases on your API
:)&lt;/p&gt;
&lt;p&gt;We can now load these flags into GHC.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-haskell&#34; data-lang=&#34;haskell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;dflags0&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getSessionDynFlags&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;dflags1&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getCabalDynFlagsLib&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pfp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dflags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parseDynamicFlags&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dflags0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;noLoc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dflags1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;setSessionDynFlags&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dflags&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Your GHC API environment is finally set up, you can now use it.&lt;/p&gt;
&lt;p&gt;We still want to retrieve the exported symbols of a module: let&amp;rsquo;s write the
corresponding GHC API code.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-haskell&#34; data-lang=&#34;haskell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getModExports&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;MonadIO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;FilePath&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ModuleName&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getModExports&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pfp&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mn&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;liftIO&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;runGhc&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Just&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;libdir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dflags0&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getSessionDynFlags&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dflags1&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getCabalDynFlagsLib&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pfp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dflags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parseDynamicFlags&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dflags0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;noLoc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dflags1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;setSessionDynFlags&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dflags&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;guessTarget&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileName&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Nothing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;setTargets&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;LoadAllTargets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;modSum&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getModSummary&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mkModuleName&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;parseModule&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modSum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;typecheckModule&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;desugarModule&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getModExports&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;modName&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;intercalate&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;components&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mn&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fileName&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;./&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;toFilePath&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mn&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;getModExports&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fmap&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getAvName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mg_exports&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dm_core_module&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Aaaaaand, that&amp;rsquo;s it, we&amp;rsquo;re done. Again: you would probably want to add a bit
more of error handling to this.&lt;/p&gt;
&lt;p&gt;As you can see, cabal-helper has a pretty simple API and is quite
straightforward to use. I just had to ignore some upper bounds to use it in my
project.&lt;/p&gt;
&lt;p&gt;Under the hood, it&amp;rsquo;s a whole other story. Since Cabal&amp;rsquo;s artifacts are
quite heavily version dependant, cabal-helper will compile and run a small
wrapper at runtime.  This wrapper is in charge to parse the artifacts and send
back the results to the main process your program is running in. Since the
wrapper has been built by the same Cabal that built the artifacts, we are
kinda sure it will be able to parse them.&lt;/p&gt;
&lt;p&gt;In our example, this does not really matter as we are sure the Cabal who built
cabal-helper is the same who built the module we are inspecting, however, this
can be a killer feature in case you are building an editor plugin.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Special thanks to [Edward Yang]
(&lt;a href=&#34;http://ezyang.com&#34;&gt;http://ezyang.com&lt;/a&gt;)
for writing an article detailing the &lt;code&gt;GHC --interactive&lt;/code&gt; trick and [Dxld]
(&lt;a href=&#34;http://darkboxed.org/&#34;&gt;http://darkboxed.org/&lt;/a&gt;) for writing cabal-helper, helping me figure out how
to use it, and relaxing some upper bounds :)&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Silver Searcher: Useful Regexes for a Haskell Code-Base</title>
      <link>https://alternativebit.fr/posts/haskell/ag/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Sun, 22 Jul 2018 17:14:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/haskell/ag/</guid>
      <description>&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;I use 4 Perl Regex patterns most of the time when it comes to search
some Haskell code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Functions:    &lt;span class=&#34;s2&#34;&gt;&amp;#34;\b&amp;lt;args&amp;gt;\b[ \t\n]+::&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Types:        &lt;span class=&#34;s2&#34;&gt;&amp;#34;(data|newtype|type)(\ +)\b&amp;lt;args&amp;gt;\b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;TypeClasses:  &lt;span class=&#34;s2&#34;&gt;&amp;#34;class(\ +)(.*)(=&amp;gt;)*(\ *)\b&amp;lt;args&amp;gt;\b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Constructors: &lt;span class=&#34;s2&#34;&gt;&amp;#34;\|[\t\ ]+\b&amp;lt;args&amp;gt;\b&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I am looking for a better way to search for a type constructor, email me at
picnoir at this domain if you have any better idea.&lt;/p&gt;
&lt;h2 id=&#34;ag&#34;&gt;Ag&lt;/h2&gt;
&lt;p&gt;Exploring an unknown code-base is always tricky: you need to somehow translate
a text-based representation of a software to an accurate  model in your own
mind. Being able to efficiently search through the code helps to reduce the
read-search loop feedback and frees a lot of headspace.&lt;/p&gt;
&lt;p&gt;Unlike many languages, Haskell does not have a real IDE and like many
developers, I use a traditional text-based searching tool: the
&lt;a href=&#34;https://github.com/ggreer/the_silver_searcher&#34;&gt;silver searcher&lt;/a&gt;. This
software is blazing fast and supports Perl regexes.&lt;/p&gt;
&lt;p&gt;After using it to search through my Haskell code for quite some time, I found
myself using the same regex patterns all day long. I soon started to write
some aliases to cover most of my searching needs. In this article, I&amp;rsquo;ll share
my favorite ones.&lt;/p&gt;
&lt;p&gt;In the following regexes, &lt;code&gt;&amp;lt;SYMBOL&amp;gt;&lt;/code&gt; will represent the symbol we&amp;rsquo;re looking
for.&lt;/p&gt;
&lt;h2 id=&#34;functions&#34;&gt;Functions&lt;/h2&gt;
&lt;p&gt;Searching for functions is quite straightforward: the function name is always
followed by a &lt;code&gt;::&lt;/code&gt; lexeme. This lexeme is separated from the symbol either by
both some whitespaces or new lines.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&amp;lt;PATTERN&amp;gt;&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\t\n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;+::&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;types&#34;&gt;Types&lt;/h2&gt;
&lt;p&gt;When looking for a type, we usually like to search for any data, newtype or
type declaration.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;newtype&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;+&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&amp;lt;PATTERN&amp;gt;&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here, we do not try to match against any constructor, we prefer having a
distinct pattern matching them.&lt;/p&gt;
&lt;h2 id=&#34;typeclass&#34;&gt;TypeClass&lt;/h2&gt;
&lt;p&gt;Same trick as when we look for a type, except we are here matching a class
lexeme.&lt;/p&gt;
&lt;p&gt;Note type classes may have some type constraints on the left side. We need
to pattern match those as well.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;class&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;+&lt;span class=&#34;o&#34;&gt;)(&lt;/span&gt;.*&lt;span class=&#34;o&#34;&gt;)(=&lt;/span&gt;&amp;gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;*&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;*&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&amp;lt;args&amp;gt;&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;data-constructor&#34;&gt;Data Constructor&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s sometimes useful to find the definition of a type constructor. The idea
here is similar to the function regex: the constructor symbol is most of the
time following the &lt;code&gt;|&lt;/code&gt; lexeme.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;\|&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\t\ &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;+&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&amp;lt;PATTERN&amp;gt;&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unlike the previous patterns, I am not quite satisfied with this one: it does
matches against guards, miss the first constructor of a regular algebraic data
type and does not match any constructor expressed in the GADT style. To be
honest, I hesitated about including this one in this article. This one could be
greatly improved.&lt;/p&gt;
&lt;h2 id=&#34;vim-integration&#34;&gt;Vim Integration&lt;/h2&gt;
&lt;p&gt;Being able to directly jump to the matched string from within the editor is
always nice. If you happen to be a heretic of some sort (ie. not a vim user),
this section will be no use for you. Just use a plain shell alias or a specific
feature from your favorite editor.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll first need a vim silver searcher integration. I personally use
&lt;a href=&#34;https://github.com/mileszs/ack.vim&#34;&gt;ack.vim&lt;/a&gt; and will assume you also use it.&lt;/p&gt;
&lt;p&gt;I use some short aliases for the patterns previously showed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;:Aghf &amp;lt;PATTERN&amp;gt;&lt;/strong&gt; Ag Haskell Function: looks for a function definition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;:Aght &amp;lt;PATTERN&amp;gt;&lt;/strong&gt; Ag Haskell Type: looks for a type definition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;:Aghtc &amp;lt;PATTERN&amp;gt;&lt;/strong&gt; Ag Haskell TypeClass: looks for a typeclass definition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;:Aghc &amp;lt;PATTERN&amp;gt;&lt;/strong&gt; Ag Haskell Constructor: looks for a type
constructor definition.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These commands are defined in my configuration using the &lt;code&gt;command!&lt;/code&gt; directive:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-vim&#34; data-lang=&#34;vim&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;complete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Aghf&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Ack&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;G&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;.*\.hs&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\b&amp;lt;args&amp;gt;\b[ \t\n]+::&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;complete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Aght&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Ack&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;G&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;.*\.hs&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(data|newtype|type)(\ +)\b&amp;lt;args&amp;gt;\b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;complete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Aghtc&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Ack&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;G&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;.*\.hs&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;class(\ +)(.*)(=&amp;gt;)*(\ *)\b&amp;lt;args&amp;gt;\b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;complete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Aghc&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Ack&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;G&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;.*\.hs&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\|[\t\ ]+\b&amp;lt;args&amp;gt;\b&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;rsquo;s pretty much all. These 4 simple aliases are probably covering around 80%
of all my search needs. I still need to perform some custom search from time to
time, but I am overall quite pleased by this small setup.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Please, Keep your Blog Light</title>
      <link>https://alternativebit.fr/posts/lightweight-blog/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/web">Web</category>
      <pubDate>Wed, 24 Jan 2018 14:11:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/lightweight-blog/</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; keeping your blog lightweight is important, I show you how to design a blog fitting in less than 10kB.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re already convinced weight &lt;strong&gt;really&lt;/strong&gt; matters when it comes to web pages? You can skip the introduction and directly see how you can reduce your blog&amp;rsquo;s weight through a &lt;a href=&#34;#a-practical-example-of-lightweight-blog&#34;&gt;practical example&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;why-does-size-matters&#34;&gt;Why Does Size Matters?&lt;/h2&gt;
&lt;p&gt;I grew up in a French isolated village during the early 2000&amp;rsquo;s. Living in an isolated place during the early 2000&amp;rsquo;s meant no public libraries, no technical bookstore, no access to computer-related knowledge or any kind of technical expertise. When my parents decided to subscribe to an RTC connection, everything changed. All of sudden, I had access to virtually an infinite amount of information about computers, RC models, and other technical fields.&lt;/p&gt;
&lt;p&gt;By reading blogs and forums, I had access to everything a geeky kid could dream of: I learned to write websites using table-based HTML, I learned to fly RC models, I learned to read English, I learned to write PHP 4 using l33t-sp3ll3d identifiers, I learned to exploit an XSS in order to bypass my monthly hosting quota, I learned that doing this would lead me to unpleasant consequences.&lt;/p&gt;
&lt;p&gt;I learned all of that using a 3.5kB/s crappy RTC connection. I was able to access these information with such ease because the internet was then mostly text pages, because web-pages were still relatively light.&lt;/p&gt;
&lt;p&gt;Fast forward 15 years, I do not use l33t identifiers anymore, I don&amp;rsquo;t fly RC-Models anymore but I still love to write computer programs! Like almost all western programmers, I spent most of my adult life in big cities where internet is cheap and fast.&lt;/p&gt;
&lt;p&gt;Eventually, I decided to take a sabbatical year. The first thing you do in such a case is cutting unnecessary expenses to make it as long as possible. So, as a &lt;a href=&#34;https://github.com/PicNoir/KeyWar&#34;&gt;grown-up man&lt;/a&gt;, I downgraded my mobile data plan to the cheapest one, the one allowing me to download 25MB monthly. I mostly use my phone for chatting and reading blog posts, I thought 25MB per month was more than enough.&lt;/p&gt;
&lt;p&gt;As I often am, I was wrong. Four days after subscribing to this plan, I received the - now familiar - &amp;ldquo;out of data&amp;rdquo; notification.&lt;/p&gt;
&lt;p&gt;What the hell!?&lt;/p&gt;
&lt;p&gt;Well, it turns out the web became heavier than I thought.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s load a medium post:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Url:    https://medium.com/[..]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3.26 MB Transferred total
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1.2  MB Html + JS + CSS + Fonts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;3.26MB per post!?&lt;/p&gt;
&lt;p&gt;At this rate, I am able to read 7 posts per month&amp;hellip; Do this article really worth a seventh of my monthly data plan? I highly doubt it.&lt;/p&gt;
&lt;p&gt;This page would take around 9 minutes to load using my RTC childhood connection.&lt;/p&gt;
&lt;p&gt;I took medium as an example as it seems to be the most popular blogging platform nowadays; keep in mind this is not an exception, this is a trend. A lot of popular blogs tends to be really heavy!&lt;/p&gt;
&lt;p&gt;You may say &amp;ldquo;it&amp;rsquo;s not a problem: RTC connections became quite rare and time travel devices &lt;a href=&#34;https://vangry.deviantart.com/art/HDR-Time-Travel-Device-76934391&#34;&gt;are still primitive&lt;/a&gt;&amp;rdquo;. You&amp;rsquo;re right, but look at the latest &lt;a href=&#34;https://www.akamai.com/fr/fr/multimedia/documents/state-of-the-internet/q1-2017-state-of-the-internet-connectivity-report.pdf&#34;&gt;akamai speed report&lt;/a&gt;. The global internet median speed is ~8Mbps: that&amp;rsquo;s around 1MB/s.&lt;/p&gt;
&lt;p&gt;This medium article, on average, will take 3 seconds to load on a device. But that&amp;rsquo;s even worse! The website is full of asynchronous requests, on a 2017 dell xps laptop, a full render of the page takes approximatively 2 seconds. Yup, this page will take 5 seconds to load on a recent high-end laptop using an average internet connection!&lt;/p&gt;
&lt;p&gt;This is a huge latency to display a simple text page on a pretty fast device!&lt;/p&gt;
&lt;p&gt;Are we doomed to increase the global web bandwidth and experience high latency for the rest of our lives? Is making something light so complicated? Let&amp;rsquo;s figure that out by making this blog as light as possible.&lt;/p&gt;
&lt;h2 id=&#34;a-practical-example-of-lightweight-blog&#34;&gt;A Practical Example of Lightweight Blog&lt;/h2&gt;
&lt;p&gt;Luckily for us, making a blog fast is more about avoiding to add unnecessary dependencies than profiling and optimizing the rendering. This game is all about being minimalistic without altering the reading ergonomics.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a pragmatical approach and think about what we need here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static website:&lt;/strong&gt; we want to deliver the blog as quickly as possible. No need to be delusional here, you won&amp;rsquo;t post regularly enough to make a dynamic website appealing :).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responsive design:&lt;/strong&gt; we want the text to be as comfortable as possible to read regardless the device used. We should at least provide a desktop, a mobile, and a tablet version of the design.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightweight:&lt;/strong&gt; The total weight of the page (HTML + assets) should not exceed 20 kB. We also should leverage browser caching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pleasing design:&lt;/strong&gt; alright, this one is totally objective. But at least, I want something appealing to me.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JavaScript and CDN free:&lt;/strong&gt; we don&amp;rsquo;t want to use any javascript rendering nor use any external CDN to serve the assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimal Effort:&lt;/strong&gt; we don&amp;rsquo;t want to spend more time designing the blog than actually writing in it. We don&amp;rsquo;t want to re-invent the wheel here. The blog should setup should take us less than 4 hours (ie. a Sunday afternoon).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;generating-your-blogs-pages&#34;&gt;Generating your Blog&amp;rsquo;s Pages&lt;/h3&gt;
&lt;p&gt;First, we need to pick a static website generator. There is no &lt;a href=&#34;https://blog.getpelican.com/&#34;&gt;lack&lt;/a&gt; of &lt;a href=&#34;https://jekyllrb.com/&#34;&gt;such&lt;/a&gt; &lt;a href=&#34;https://jaspervdj.be/hakyll/&#34;&gt;software&lt;/a&gt;, just pick one. I chose &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt;: its documentation is nice and it supports server-side syntax highlighting.&lt;/p&gt;
&lt;p&gt;We then need to write a theme for our blog. I would advise starting one from scratch, it&amp;rsquo;ll be quicker: templates tend to be quite bloated and having many dependencies.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not going to dive into the details for the whole HTML/CSS part, but rather just give you an overview of what you need to write your design without loading tons of external libraries. If you have any problem related to web-design, &lt;a href=&#34;https://developer.mozilla.org/fr/&#34;&gt;MDN&lt;/a&gt; is usually a good starting point.&lt;/p&gt;
&lt;p&gt;If you did not have a look at web dev for the last few years, I have a good news for you: you don&amp;rsquo;t really need a CSS framework anymore! You can thank CSS&amp;rsquo;s flex boxes; this feature is now supported by almost all active browsers and is basically all you need to organize your HTML elements. If you don&amp;rsquo;t know how to use them, the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox&#34;&gt;introduction written by Mozilla&lt;/a&gt; is what you&amp;rsquo;re looking for!&lt;/p&gt;
&lt;p&gt;In order to adapt the design for smartphones and tablets, we need to use &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries&#34;&gt;media queries&lt;/a&gt;. Contrary to popular belief, they are pretty simple to use: three rules according to the screen width will get you covered for most usual devices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;gt; 1000px&lt;/code&gt;: desktop and big screens.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;880 &amp;gt; width &amp;gt; 1000px&lt;/code&gt;: tablets, netbooks and small screens.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt; 880&lt;/code&gt;: smartphones.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can easily debug your media queries related code using a device emulator. It&amp;rsquo;s included in both firefox and chrome web-dev tools.&lt;/p&gt;
&lt;p&gt;Setting up an appropriate text width is primordial. Reading a text being too wide or too narrow is really uncomfortable. The desired width of the text will depend on the device; on a mobile phone, you want to use as much space as possible, while on a desktop, you want to display your text in a narrow centered column.&lt;/p&gt;
&lt;p&gt;I used the following rules for this blog (vw being the viewport width):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;media&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;min-width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;1000px&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;vw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;media&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;min-width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;800px&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;max-width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;1000px&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;70&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;vw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;media&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;max-width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;800px&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;95&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;vw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Regarding fonts, I made the heavy opinionated choice to only use the sans serif one embedded in the reader&amp;rsquo;s system.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;(..)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;font-family&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;sans-serif&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;(..)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On one hand, the font will differ depending on the system, on the other hand, it will reduce the assets size. But as I said earlier, it is a quite heavy opinionated choice, default fonts can be quite uncomfortable to read, especially on old Windows systems.&lt;/p&gt;
&lt;p&gt;[2022 edit]: I ended up reverting that decision and serve a web font (IBM Plex Sans). The default sans system font is sadly severely out of date wrt. modern visual standards on a lot of systems :(&lt;/p&gt;
&lt;p&gt;I avoided using images as much as possible. I actually ended up having one image for this theme: the small icon displayed in your browser tab.&lt;/p&gt;
&lt;p&gt;To illustrate the article, diagrams often help. Using SVG is a good way to keep everything light without sacrificing diagrams. Using &lt;a href=&#34;https://inkscape.org/en/&#34;&gt;Inkscape&lt;/a&gt; is a good way to edit those diagrams. Don&amp;rsquo;t forget to export the project to &amp;ldquo;standard SVG&amp;rdquo; or you will dump some private metadata along with your image.&lt;/p&gt;
&lt;p&gt;Having the whole article included in the RSS feed is quite convenient: it gives the reader the option of not using your website&amp;rsquo;s stylesheet. Sadly, Hugo&amp;rsquo;s default RSS feed only renders the first ~100 words of each article. Here&amp;rsquo;s a small template overriding this behavior.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;% cat /layouts/posts/rss.xml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;rss&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;version=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;2.0&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;xmlns:atom=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http://www.w3.org/2005/Atom&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;channel&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ .Title}} &lt;span class=&#34;nt&#34;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;link&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Recent posts&lt;span class=&#34;nt&#34;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;generator&amp;gt;&lt;/span&gt;Hugo -- gohugo.io&lt;span class=&#34;nt&#34;&gt;&amp;lt;/generator&amp;gt;&lt;/span&gt;{{ with .Site.LanguageCode }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;language&amp;gt;&lt;/span&gt;{{.}}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/language&amp;gt;&lt;/span&gt;{{end}}{{ with .Site.Author.email }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;managingEditor&amp;gt;&lt;/span&gt;{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/managingEditor&amp;gt;&lt;/span&gt;{{end}}{{ with .Site.Author.email }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;webMaster&amp;gt;&lt;/span&gt;{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/webMaster&amp;gt;&lt;/span&gt;{{end}}{{ with .Site.Copyright }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;copyright&amp;gt;&lt;/span&gt;{{.}}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/copyright&amp;gt;&lt;/span&gt;{{end}}{{ if not .Date.IsZero }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;lastBuildDate&amp;gt;&lt;/span&gt;{{ .Date.Format &amp;#34;Mon, 02 Jan 2006 15:04:05 -0700&amp;#34; | safeHTML }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/lastBuildDate&amp;gt;&lt;/span&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    {{ with .OutputFormats.Get &amp;#34;RSS&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        {{ printf &amp;#34;&lt;span class=&#34;nt&#34;&gt;&amp;lt;atom:link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;%q&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;\&amp;#34;self\&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;%q&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;&amp;#34; .Permalink .MediaType | safeHTML }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    {{ range .Data.Pages }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;link&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;pubDate&amp;gt;&lt;/span&gt;{{ .Date.Format &amp;#34;Mon, 02 Jan 2006 15:04:05 -0700&amp;#34; | safeHTML }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      {{ with .Site.Author.email }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;author&amp;gt;&lt;/span&gt;{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;{{end}}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;guid&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/guid&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;{{ .Content | html }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/channel&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/rss&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Don&amp;rsquo;t forget to add a link to the RSS feed both in the footer and the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/posts/index.xml&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;alternate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;application/rss+xml&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alright, now we designed everything, we are almost done.&lt;/p&gt;
&lt;p&gt;We need to give our assets a unique name. We&amp;rsquo;ll need to reflect any change to their name to properly leverage the browser cache. You have two options: either you fire npm and setup an asset minifier, either you simple prepend &lt;code&gt;DDMMYYYY&lt;/code&gt; on each filename like me :).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s not kid ourselves, we are going to alter this theme twice a year, automating that won&amp;rsquo;t gain any time.&lt;/p&gt;
&lt;h3 id=&#34;distributing-the-content&#34;&gt;Distributing the Content&lt;/h3&gt;
&lt;p&gt;Now your blog is generated on your computer, it is time to publish it on the web. You have two major choices here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You do not want to spend any money on it: just push it on &lt;a href=&#34;https://pages.github.com/&#34;&gt;GitHub pages&lt;/a&gt; and you&amp;rsquo;re done. You can skip this  and directly read the &lt;a href=&#34;#analytics&#34;&gt;next section&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You want to deliver it from your *nix server.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First of all, you want to serve the blog on a TLS-only link. Maybe your ISP/State does not alter your HTTP traffic, but it&amp;rsquo;s not the &lt;a href=&#34;https://www.theregister.co.uk/2016/07/14/cloudflare_investigating_mystery_interception_of_site_traffic_across_india/&#34;&gt;case&lt;/a&gt; &lt;a href=&#34;https://justinsomnia.org/2012/04/hotel-wifi-javascript-injection/&#34;&gt;everywhere&lt;/a&gt;. Please, think about your potentially endangered readers and protect them from a nasty rogue javascript injection.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt; is a free and entirely automated TLS certificate provider. The first issuing will need some &lt;a href=&#34;https://www.nginx.com/blog/free-certificates-lets-encrypt-and-nginx/&#34;&gt;manual configuration&lt;/a&gt;, but afterward, all you&amp;rsquo;ll need for a cert renewal will be a simple cron rule fired twice a month.&lt;/p&gt;
&lt;p&gt;We then need to configure Nginx to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;redirect HTTP requests to the HTTPS endpoint.&lt;/li&gt;
&lt;li&gt;include the &lt;a href=&#34;https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security&#34;&gt;HSTS policy&lt;/a&gt; header.&lt;/li&gt;
&lt;li&gt;support IPv6 and HTTP2.&lt;/li&gt;
&lt;li&gt;deliver the static content with a specific HTTP header telling that clients should forever cache the content.&lt;/li&gt;
&lt;li&gt;GZIP the text-based content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s a quick dump of my configuration. You can use it as a starting point:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt;         &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt;    &lt;span class=&#34;s&#34;&gt;alternativebit.fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;/.well-known/acme-challenge&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;default_type&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;text/plain&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt;        &lt;span class=&#34;s&#34;&gt;/tmp/letsencrypt-auto/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;return&lt;/span&gt;         &lt;span class=&#34;mi&#34;&gt;301&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;https://alternativebit.fr&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$request_uri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ssl&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http2&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;default_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;[::]:443&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ssl&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http2&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;default_server&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ipv6only=on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;ssl_certificate&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$PATH_TO/fullchain.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;ssl_certificate_key&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$PATH_TO/privkey.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;alternativebit.fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;client_max_body_size&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;autoindex&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$PATH_TO_FILES&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Cache-Control&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;no-cache,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;no-store,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;must-revalidate&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;max-age=31536000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;includeSubDomains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;preload&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;.(jpg|jpeg|png|gif|ico|css|js)&lt;/span&gt;$ &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Cache-Control&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;public,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;max-age=31536000&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;max-age=31536000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;includeSubDomains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;preload&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;c1&#34;&gt;# Gzip black magic
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip_disable&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;msie6&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip_vary&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip_proxied&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip_comp_level&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip_buffers&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip_http_version&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip_types&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/plain&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/css&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/json&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/x-javascript&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml+rss&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/javascript&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;analytics&#34;&gt;Analytics&lt;/h3&gt;
&lt;p&gt;Please, don&amp;rsquo;t.&lt;/p&gt;
&lt;h2 id=&#34;you-now-can-open-your-text-editor&#34;&gt;You Now Can Open your Text Editor&lt;/h2&gt;
&lt;p&gt;So, is this mission successful? Let&amp;rsquo;s dig into the metrics.&lt;/p&gt;
&lt;p&gt;All the following tests have been conducted on the very same page hosted on the very same HTTP server you are currently reading this post.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first test the first page loading (no browser caching involved).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Transferred: ~9kB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Unzipped Size: ~25kB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Finish: ~200ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yup, the whole content + assets fits just under 9kB and takes less than 500ms to be both downloaded and fully rendered on my laptop.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s load the same page a second time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Transferred: ~7.3kB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Unzipped Size: ~25kB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Finish: ~200ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now browser caching is leveraged, only 7.3kB has been transferred!&lt;/p&gt;
&lt;p&gt;It would take ~2 second to download this long page from my childhood RTC line! The latency is way lower than on a typical medium post; I guess we can say this experiment is a success!&lt;/p&gt;
&lt;p&gt;As you can see, making a light low-latency blogging system is quite easy. All the above experiment has been implemented on a Sunday afternoon. Less than 3 hours if we exclude the NGINX configuration black magic.&lt;/p&gt;
&lt;p&gt;If you have a blog and are actively using it, please, invest those 3 hours and contribute to make the web a better place :)&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Bracket: a Tale of Partially Applied Functions</title>
      <link>https://alternativebit.fr/posts/haskell/bracket/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Wed, 29 Nov 2017 11:21:00 +0100</pubDate>
      <guid>https://alternativebit.fr/posts/haskell/bracket/</guid>
      <description>&lt;h3 id=&#34;tldr&#34;&gt;TL;DR&lt;/h3&gt;
&lt;p&gt;In this post, we describe how we can use partially applied functions as a design building block though the study of a practical example: the &lt;a href=&#34;http://hackage.haskell.org/package/base-4.10.0.0/docs/Control-Exception-Base.html###v:bracket&#34;&gt;bracket&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll use the Haskell programming language to illustrate this post. Just keep in mind this could be applied to almost any language.&lt;/p&gt;
&lt;h3 id=&#34;it-all-begins-with-code-reuse&#34;&gt;It all Begins with Code Reuse&lt;/h3&gt;
&lt;p&gt;HSpec is a &lt;a href=&#34;https://fr.wikipedia.org/wiki/Behavior-driven_development&#34;&gt;BDD-style&lt;/a&gt; unit-test framework for Haskell. In that kind of tests,
it is quite common to create and destroy resources such as database handles, complex data structures, an HTTP server, etc.&lt;/p&gt;
&lt;p&gt;I was looking for a way to share a resource across several &lt;a href=&#34;https://hackage.haskell.org/package/hspec-2.4.4/docs/Test-Hspec.html###v:it&#34;&gt;&lt;code&gt;it&lt;/code&gt;&lt;/a&gt; clauses without having to duplicate its instantiate/tear-down process. I quickly found the &lt;code&gt;around&lt;/code&gt; function and have been instantly confused by its type.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;around&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;ActionWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SpecWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SpecWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;ActionWith&lt;/code&gt; and &lt;code&gt;SpecWith&lt;/code&gt; being some alias types, let me de-sugar it for you.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;around&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SpecM&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SpecM&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I know, strange right?&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;SpecM&lt;/code&gt; monad is a reader monad used internally by HSpec, this reader monad contains the newly injected resource among other things. We can omit that part for now.&lt;/p&gt;
&lt;p&gt;If your mouth started foaming after &amp;ldquo;reader monad&amp;rdquo; (&lt;a href=&#34;https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/&#34;&gt;it shouldn&amp;rsquo;t&lt;/a&gt;), you can just picture it as a small read-only key-value database.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s really interesting here is this part:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What is this thing?&lt;/p&gt;
&lt;p&gt;Well, to answer that, we first need to understand the &lt;code&gt;bracket&lt;/code&gt; function.&lt;/p&gt;
&lt;h3 id=&#34;the-bracket-function&#34;&gt;The Bracket Function&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s have a look at its type:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s keep in mind its purpose: it&amp;rsquo;s a way to create and destruct a resource with side effects, preventing any leak after using it.&lt;/p&gt;
&lt;p&gt;Despite being a bit intimidating, this signature actually makes sense.&lt;/p&gt;
&lt;p&gt;As explained in &lt;a href=&#34;https://wiki.haskell.org/Bracket_pattern&#34;&gt;this Haskel wiki page&lt;/a&gt;, the above type signature can be split into 3 parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IO a&lt;/strong&gt;: the constructor. This function will return the resource &lt;code&gt;a&lt;/code&gt; in a IO monad.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;(a -&amp;gt; IO b)&lt;/strong&gt;: the destructor. This function will take as parameter the previously created &lt;code&gt;a&lt;/code&gt; resource and will apply the necessary IO actions to destroy it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;(a -&amp;gt; IO c)&lt;/strong&gt;: the actual effect-full computation that will use the &lt;code&gt;a&lt;/code&gt; resource.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IO c&lt;/strong&gt;: the final result coming from the previous computation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;understanding-the-around-function&#34;&gt;Understanding the Around Function&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s go back to the &lt;code&gt;around&lt;/code&gt; function, it just makes sense now: it is a partially applied bracket function. A bracket function to which we already provided a constructor and a destructor.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;around&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt;                         &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SpecM&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SpecM&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;bracket&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ah! Pretty awesome don&amp;rsquo;t you think?&lt;/p&gt;
&lt;p&gt;Personally, I&amp;rsquo;m found of this small simple function. What a great simple way to express a resource life cycle!&lt;/p&gt;
&lt;h3 id=&#34;a-practical-example-with-hspec&#34;&gt;A Practical Example with HSpec&lt;/h3&gt;
&lt;p&gt;Alright, now we understand the handle function signature, let&amp;rsquo;s see how we can use it in HSpec.&lt;/p&gt;
&lt;p&gt;Let say we want to test our implementation of the great Star Wars names database.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;describe&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;getStarWarsNameDb&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;contains Star Wars characters names&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getStarWarsNameDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Dark Vader&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldNotBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;JarJar Beans&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;clear&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;contains Star Wars citations&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getStarWarsNameDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;May the fourth be with you.&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Do. Or do not. There will be a trial.&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;clear&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, we have some code duplication here, we want to get rid of both the construction and the destruction of the &lt;code&gt;db&lt;/code&gt; database.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s create a partially applied bracket call to which we already provided both a constructor and a destructor:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;withDb&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;StarWarsDb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;withDb&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bracket&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getStarWrsNameDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clear&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We then just need to adapt each &lt;code&gt;it&lt;/code&gt; clause to be pattern matched against a function having a single argument: the database handle.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;around&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;withDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;describe&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;getStarWarsNameDb&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;contains Star Wars characters names&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Dark Vader&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldNotBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;JarJar Beans&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;contains Star Wars citations&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;May the fourth be with you.&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Do. Or do not. There will be a trial.&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Neat, right!&lt;/p&gt;
&lt;p&gt;We could go even further thanks to the polymorphic nature of the around function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;withDb&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(((&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;StarWarsDb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;StarWarsDbContent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;withDb&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bracket&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getDbContent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clear&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hDb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;with&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;getDbContent&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;hdb&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;createDb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hdb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hdb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;around&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;withDb&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;describe&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;getStarWarsNameDb&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;contains Star Wars characters names&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hdb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Dark Vader&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldNotBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;JarJar Beans&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;contains Star Wars citations&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hdb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;May the fourth be with you.&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Do. Or do not. There will be a trial.&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See, all we needed to do was to alter the withDb function and the pattern matching of the anonymous functions used in the in clause.&lt;/p&gt;
&lt;h3 id=&#34;wrap-up&#34;&gt;Wrap Up&lt;/h3&gt;
&lt;p&gt;Partially applied functions are an amazing tool. Thanks to them, we can add context little by little to a function call. Used properly, this can lead to some nice design tricks such as the &lt;code&gt;bracket&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Hope you enjoyed this ode to the bracket function :). We are constantly taking for granted some beautiful functions such as this one. I think it is important to both acknowledge and celebrate that, sometimes, software can be beautiful!&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Writing a Twitch Overlay using Haskell</title>
      <link>https://alternativebit.fr/posts/keywar/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Sat, 21 Oct 2017 12:48:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/keywar/</guid>
      <description>&lt;p&gt;I have been watching &lt;a href=&#34;https://www.twitch.tv/jessicamak&#34;&gt;Jessica&amp;rsquo;s Mak&lt;/a&gt; streams lately. She is an indie game developper, but most of all, she has a kick ass overlay that shows what she is typing in real time.&lt;/p&gt;
&lt;p&gt;I wanted the same one, I made it using Haskell, &lt;a href=&#34;https://hackage.haskell.org/package/gloss&#34;&gt;Gloss&lt;/a&gt; and &lt;a href=&#34;http://chipmunk-physics.net/&#34;&gt;Chipmunk&lt;/a&gt; via the &lt;a href=&#34;https://hackage.haskell.org/package/Hipmunk&#34;&gt;Hipmunk&lt;/a&gt; binding.&lt;/p&gt;
&lt;p&gt;Long story short, it ended up looking like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/keywar/keywar-demo.gif&#34; alt=&#34;Demo Gif&#34;&gt;&lt;/p&gt;
&lt;p&gt;The source code is available in this &lt;a href=&#34;https://github.com/PicNoir/KeyWar&#34;&gt;git repository&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;gloss-display-library&#34;&gt;Gloss, display library&lt;/h1&gt;
&lt;p&gt;Gloss is a graphics library built upon OpenGL. The API is high level enough to mask a lot of low level nastiness.&lt;/p&gt;
&lt;p&gt;To keep it short, the API is based around two functions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An update function, which processes the input events and update the world data structure.&lt;/li&gt;
&lt;li&gt;A display function, which renders the world as a Picture.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Those two functions are using two datastructures:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The world datastructure. This structure can be what you want, you will define it by yourself.&lt;/li&gt;
&lt;li&gt;The Picture datastructure. It represents something that can be drawed to the screen.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I really like this second datastructure, its shape is highly interesting, let&amp;rsquo;s dig in it.&lt;/p&gt;
&lt;h1 id=&#34;hipmunk-physics-library&#34;&gt;Hipmunk, physics library&lt;/h1&gt;
&lt;p&gt;Each object is composed by both a body and a shape. The body contains the physics informations (mass, moment of momentum, elasticity, &amp;hellip;) while the shape just describes the bounding box of the object.&lt;/p&gt;
&lt;h1 id=&#34;capturing-the-input&#34;&gt;Capturing the Input&lt;/h1&gt;
&lt;p&gt;Now we can display funny bouncy letters, we still need to fix a problem: how to capture keyboard&amp;rsquo;s input? We were directly using the Gloss input system for debugging purposes. This sadly only work when the keywar window is focussed. Xorg only transferts the input events to the currently focussed window, which, in our case, is a problem.&lt;/p&gt;
&lt;p&gt;How could we capture &lt;strong&gt;all&lt;/strong&gt; the input events, regardless the focussed window? I found 3 solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We could create an overlay window transferring the events to the underlying ones. This solution would imply to re-implement a subset of my window-manager features. We do not want to re-invent the wheel once again :).&lt;/li&gt;
&lt;li&gt;We could bypass xorg and directly read Linux&amp;rsquo;s /dev/inputX input events. This solution is tricky, not only those events are using a binary data format, which make parsing them clumsy, but those events are raw, they are not applied to any keymap. If we really wanted to read those events, not only we should write the appropriate parser, but we also should support different keymaps.&lt;/li&gt;
&lt;li&gt;We use a keylogger and &lt;code&gt;tail -f&lt;/code&gt; its output.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last solution was the simplest, we used the &lt;a href=&#34;https://github.com/kernc/logkeys&#34;&gt;logkeys&lt;/a&gt; keylogger.&lt;/p&gt;
&lt;h2 id=&#34;side-note-passwords&#34;&gt;Side Note: Passwords&lt;/h2&gt;
&lt;p&gt;As mentionned in the github readme, you need to be extra careful when typing passwords, you clearly do not want to leak them while streaming.&lt;/p&gt;
&lt;p&gt;I added a system which displays question marks in place of the actual letters to mitigate that. The audience will get the lenght of your password, but it is already the case if your keyboard is a bit noisy. Mine is noisy as hell, hence this is not really a problem to me.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Wireguard-Haskell: Getting Started</title>
      <link>https://alternativebit.fr/posts/wireguard/wireguardrpc/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Mon, 16 Oct 2017 09:48:00 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/wireguard/wireguardrpc/</guid>
      <description>&lt;h1 id=&#34;why-starting-this-project&#34;&gt;Why Starting this Project?&lt;/h1&gt;
&lt;p&gt;After finishing DobadoBots, I was looking for a Haskell project in which I could be confronted with some performance and parallelism problems.&lt;/p&gt;
&lt;p&gt;Wireguard seemed to be the perfect project for that. At the time, no userspace implementation was available, the specification seemed to be simple enough for being implemented by a single person in a couple of months.&lt;/p&gt;
&lt;p&gt;Furthermore, a work in progress Haskell implementation called Nara was already available. The only contributor to this project sadly abandoned it, undocumented, several months ago. This is a great opportunity for me to complete what he started.&lt;/p&gt;
&lt;h1 id=&#34;starting-point&#34;&gt;Starting Point&lt;/h1&gt;
&lt;p&gt;The userspace implementation is not providing a CLI by itself. Instead, we are using the &lt;code&gt;wg&lt;/code&gt; CLI utility to communicate with the VPN daemon through a Unix socket.&lt;/p&gt;
&lt;p&gt;The overall architecture looks like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/wireguard/nara-overview.svg%22&#34; alt=&#34;Nara architecture overview&#34;&gt;&lt;/p&gt;
&lt;p&gt;We first need to test this RPC interface implementation. It was sadly broken. The RPC protocol changed and these changes were not reflected in the Haskell implementation.&lt;/p&gt;
&lt;p&gt;We clearly need to first re-implement and test this interface.&lt;/p&gt;
&lt;h1 id=&#34;refactoring-the-rpc-interface&#34;&gt;Refactoring the RPC Interface&lt;/h1&gt;
&lt;p&gt;The new protocol specified &lt;a href=&#34;https://www.wireguard.com/xplatform/&#34;&gt;here&lt;/a&gt; is text-based.
Both the Wireguard device and peers are described using a several key/value couples.&lt;/p&gt;
&lt;p&gt;We process the incoming data as a &lt;a href=&#34;https://hackage.haskell.org/package/conduit&#34;&gt;Conduit&lt;/a&gt; stream. Conduit comes with the conduit-attoparsec module, which let us parse the incoming stream using an Attoparsec monadic parser.&lt;/p&gt;
&lt;p&gt;Attoparsec is based around the same concept as Parsec. The main difference being that while Parsec aims to parse user-generated inputs, Attoparsec targets machine to machine data formats. It is hence faster, at the cost of a weaker error reporting system.&lt;/p&gt;
&lt;p&gt;You can find the implementation details of this parser in Nara&amp;rsquo;s &lt;a href=&#34;https://github.com/PicNoir/wireguard-hs/blob/master/src/Network/WireGuard/Internal/RpcParsers.hs&#34;&gt;git repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The four main takeaways from this implementation are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Parsing an IP address is not a trivial task. You need to be extra cautious, especially while parsing an IPv6 address.&lt;/li&gt;
&lt;li&gt;Do not hesitate to create a lot of sub-parsers. It makes the code both easily testable and more readable.&lt;/li&gt;
&lt;li&gt;You need to be extra cautious with your test cases. Test extensively the edge cases. It is dead easy to screw things up when using a Parsec-like library.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&#34;http://hackage.haskell.org/package/base-4.10.0.0/docs/Control-Applicative.html#t:Alternative&#34;&gt;Alternative&lt;/a&gt; typeclass will hold your back while dealing with optional repetitive options. This typeclass is implemented by the Parser type: you can use it with everywhere.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;next-steps&#34;&gt;Next steps&lt;/h1&gt;
&lt;p&gt;We need to re-implement the queue system. As it is, the packets are multiplexed in one TUN queue and one UDP queue. It creates a lot of latency in case of a handshake: we need the handshake to successfully complete before sending any data, regardless the peer we want to send the data to.&lt;/p&gt;
&lt;p&gt;We first need to design a new queue system. When implementing it, we will need to test it extra carefully. It may be a good excuse to dive into property-based testing techniques.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>DobadoBots: Project Wrap Up</title>
      <link>https://alternativebit.fr/posts/dobadobots/dobadowrappingup/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Thu, 28 Sep 2017 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/dobadobots/dobadowrappingup/</guid>
      <description>&lt;p&gt;Yet another post about DobadoBots: my programming video-game. Right, let&amp;rsquo;s face it: it is done for two months now, this blog post is long overdue!&lt;/p&gt;
&lt;p&gt;The video-game is now completely playable, I reached the MVP stage. Here&amp;rsquo;s a short video presenting the final result.&lt;/p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/Lb8V3ujVHMc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;p&gt;Let&amp;rsquo;s debrief this project.&lt;/p&gt;
&lt;h1 id=&#34;getting-familiar-with-haskell&#34;&gt;Getting Familiar with Haskell&lt;/h1&gt;
&lt;p&gt;The main goal was to get some practical experience with Haskell. I had literally zero real-world experience before that.&lt;/p&gt;
&lt;p&gt;After two months in, I can use Haskell productively enough. Yup, that&amp;rsquo;s a pretty steep and long learning curve. But it worth mentioning that I had no prior experience using a purely functional language.&lt;/p&gt;
&lt;p&gt;It worth mentioning that you do not need to understand what&amp;rsquo;s a free monad or what are GADTs to be productive. You do not need a category theory PHD either.&lt;/p&gt;
&lt;p&gt;There is a huge gap between getting productive enough and understanding what&amp;rsquo;s discussed on Haskell&amp;rsquo;s subreddit.
This &lt;a href=&#34;https://patrickmn.com/software/the-haskell-pyramid/&#34;&gt;blog post&lt;/a&gt; made that click for me.&lt;/p&gt;
&lt;p&gt;The main takeaway after this first practical experience is that a strong type system is a huge asset while refactoring. Having used Python quite extensively, a massive refactoring was always painful. You could easily introduce a lot of bugs as soon as your test suite was a bit weak. Haskell type system tends to cover your back most of the time and just refuse to compile your poorly refactored code.&lt;/p&gt;
&lt;p&gt;I also found that assessing a task complexity very hard. Some things that would have been really long to do using a procedural language turns out being trivial using Haskell while some task that would have been simple using Python turns out being quite complicated using Haskell.&lt;/p&gt;
&lt;h1 id=&#34;what-has-been-unexpectedly-easy&#34;&gt;What has Been Unexpectedly Easy?&lt;/h1&gt;
&lt;p&gt;As &lt;a href=&#34;https://alternativebit.fr/posts/dobadobots/dobadolanguage/&#34;&gt;previously discussed&lt;/a&gt;, I wrote my own language and parser for this project. This part has been unexpectedly easy.&lt;/p&gt;
&lt;p&gt;Using &lt;a href=&#34;https://hackage.haskell.org/package/parsec&#34;&gt;Parsec&lt;/a&gt; for the parser part and &lt;a href=&#34;https://hackage.haskell.org/package/pretty&#34;&gt;Pretty&lt;/a&gt; for the pretty printer part made writing that nearly trivial. I discussed more in depth the parser implementation &lt;a href=&#34;https://alternativebit.fr/posts/dobadobots/dobadoparser/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;what-has-been-unexpectedly-hard&#34;&gt;What has Been Unexpectedly Hard?&lt;/h1&gt;
&lt;p&gt;Retrospectively speaking, the game part has been the hardest part. Without thinking about it, I basically wrote a whole game engine&amp;hellip;&lt;/p&gt;
&lt;p&gt;Choosing SDL2 as a graphics library turned out to be a poor choice. The library is low level, you basically need to implement everything. Turns out a video-game is not a trivial piece of software, you need to implement a button system, a menu engine, etc.&lt;/p&gt;
&lt;p&gt;Writing the &lt;a href=&#34;https://alternativebit.fr/posts/dobadobots/dobadoeditor/&#34;&gt;text editor&lt;/a&gt; also turned out being a real pain that took me almost two complete weeks.&lt;/p&gt;
&lt;p&gt;The complexity is mainly hidden in small details.&lt;/p&gt;
&lt;h1 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next?&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;m not going to lie, wrapping up this MVP was painful. I do not want to spend any more time on this project, working on it actually became a burden.&lt;/p&gt;
&lt;p&gt;I do not plan to maintain this code, hence, I will not provide any binary distribution. You can, however, compile the Github repository if you want to play this game by yourself! It still has some rough edges and is only shipped with two stages. But you can easily add some more simply by looking at the JSON level format.&lt;/p&gt;
&lt;p&gt;After getting familiar with Haskell, I think it is time to dive deeper in it and try doing something involving more performance tuning and some concurrent code. I&amp;rsquo;m going to take over the &lt;a href=&#34;https://github.com/WireGuard/wireguard-hs&#34;&gt;Haskell Wireguard implementation&lt;/a&gt; for the next few months!&lt;/p&gt;
</description>
    </item>
    <item>
      <title>DobadoBots: Writing a Text Editor</title>
      <link>https://alternativebit.fr/posts/dobadobots/dobadoeditor/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Mon, 04 Sep 2017 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/dobadobots/dobadoeditor/</guid>
      <description>&lt;p&gt;Yup, yet another post on my programming videogame: Dobadobots.&lt;/p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/Lb8V3ujVHMc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;p&gt;Today, we are going to dig into the editor&amp;rsquo;s implementation.&lt;/p&gt;
&lt;h2 id=&#34;motivations&#34;&gt;Motivations&lt;/h2&gt;
&lt;p&gt;I wanted the game to be as enjoyable as possible, I wanted a quick write/feedback loop. Using an external editor would have killed this fast feedback loop, it was just not an option.&lt;/p&gt;
&lt;p&gt;I first thought about integrating a pre-existing editor in it.&lt;/p&gt;
&lt;p&gt;An option would have been to create a web-view and embedding an HTML5 based text editor. After writing a quick proof of concept, I ran into some problems: I did not find any lightweight way to make the editor communicate with the core Haskell application. The only solution I found would have been to make this happen by running a WebSocket server. This solution seemed totally overkill, I decided to dismiss it.&lt;/p&gt;
&lt;p&gt;Another option would have been to find a library implementing the basic features of an editor. However, I have not been able to find any Haskell/SDL2 text editor. Perhaps this tech stack is a bit too much specific! :)&lt;/p&gt;
&lt;p&gt;So, being unable to find anything meeting my requirements, I ended up writing my own editor.&lt;/p&gt;
&lt;h2 id=&#34;the-editors-architecture&#34;&gt;The Editor&amp;rsquo;s Architecture&lt;/h2&gt;
&lt;p&gt;As usual I started by specifying the data types and, as usual, we encapsulate the editor state in a proper record structure.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;EditorState&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;EditorState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;               &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;cursorColumn&lt;/span&gt;       &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;cursorLine&lt;/span&gt;         &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;deriving&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It encapsulates two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The position of the cursor.&lt;/li&gt;
&lt;li&gt;the text contained in the editor&amp;rsquo;s buffer.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In addition to this state, we also need to generate some SDL textures we will use to display both the text and the various editor elements. I ended embedding these elements to the renderer state which already contains most of the game textures.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;RendererState&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;RendererState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;editorCursor&lt;/span&gt;       &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Texture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;V2&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;codeTextures&lt;/span&gt;       &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Texture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;V2&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;parseErrorMess&lt;/span&gt;     &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Texture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;V2&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;parseErrorCursor&lt;/span&gt;   &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Texture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SDL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;V2&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;editor&lt;/span&gt;             &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;EditorState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can see the code is stored in multiples textures: one per code line.&lt;/p&gt;
&lt;h2 id=&#34;text-display-and-typefont-generator&#34;&gt;Text Display and Typefont Generator&lt;/h2&gt;
&lt;p&gt;We need to display the text on the screen. The code text is basically some UTF-8 characters separated by spaces and newlines.&lt;/p&gt;
&lt;p&gt;In order to display some text using &lt;code&gt;SDL2&lt;/code&gt;, you first need to create an OpenGL texture representing this text. To do that, you want to use a typefont generator. It is basically a program which generates an OpenGL texture according to a TTF font and a UTF-8 input text. In this project, I used the &lt;code&gt;SDL2-TTF&lt;/code&gt; library.&lt;/p&gt;
&lt;p&gt;This typefont generator is distributed as a C library.&lt;/p&gt;
&lt;p&gt;Some Haskell bindings were already available, hence, I did not even need to write the corresponding FFI files. The tricky part for using this library was to use it in the continuous integration runner.&lt;/p&gt;
&lt;p&gt;I use Travis as a CI, which sadly uses an outdated ubuntu. This ubuntu does not provide any kind of pre-compiled &lt;code&gt;SDL2-TTF&lt;/code&gt; binary, you need it to build it by yourself.&lt;/p&gt;
&lt;p&gt;You will need libgl1-mesa-dev to build it. Because of Travis&amp;rsquo;s exoteric path configuration, you will also need to add the &lt;code&gt;/usr/local/lib&lt;/code&gt; directory to the &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the corresponding &lt;code&gt;.travis.yml&lt;/code&gt; configuration section.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-YAML&#34; data-lang=&#34;YAML&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;before_install&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;sudo apt-get install -y libgl1-mesa-dev&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;wget http://libsdl.org/release/SDL2-2.0.5.tar.gz -O - | tar xz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;cd SDL2-2.0.5 &amp;amp;&amp;amp; ./configure &amp;amp;&amp;amp; make -j &amp;amp;&amp;amp; sudo make install &amp;amp;&amp;amp; cd ..&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;wget https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-2.0.14.tar.gz -O - | tar xz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;cd SDL2_ttf-2.0.14 &amp;amp;&amp;amp; ./configure &amp;amp;&amp;amp; make -j &amp;amp;&amp;amp; sudo make install &amp;amp;&amp;amp; cd ..&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;rendering-the-editor&#34;&gt;Rendering the Editor&lt;/h2&gt;
&lt;p&gt;The renderer is in charge of both input events handling and graphics drawing.&lt;/p&gt;
&lt;p&gt;We need to be compatible with both Unixes and Windows systems. SDL2 has been used as a hardware abstraction layer here.&lt;/p&gt;
&lt;p&gt;The renderer is directly plugged into the main graphic event loop which looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check any kind of input.&lt;/li&gt;
&lt;li&gt;Potentially append the event to the editor state.&lt;/li&gt;
&lt;li&gt;Check the syntax of the code generated by the new event.&lt;/li&gt;
&lt;li&gt;If the syntax is valid, load the new AST in the game engine.&lt;/li&gt;
&lt;li&gt;Generate the new text textures.&lt;/li&gt;
&lt;li&gt;Draw everything on the screen.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more details about this implementation, you can check &lt;a href=&#34;https://github.com/PicNoir/DobadoBots/blob/master/src/DobadoBots/Graphics/Editor.hs#L90&#34;&gt;the implementation itself&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;syntax-error-checking&#34;&gt;Syntax Error Checking&lt;/h2&gt;
&lt;p&gt;To keep the test/evaluation feedback loop short, we include a syntax checker directly in the editor.&lt;/p&gt;
&lt;p&gt;Thanks to the parser (see the &lt;a href=&#34;https://alternativebit.fr/posts/dobadobots/dobadoparser/&#34;&gt;previous post&lt;/a&gt;), this task was quite straightforward.&lt;/p&gt;
&lt;p&gt;Parsec not only returns a short text describing the error and the potential fix but also returns a position (a column line couple) helping to localize the error. Using this localization, you can highlight this part while adding an error message at the bottom of the editor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/dobadoBots/editorError.png&#34; alt=&#34;Editor&amp;rsquo;s syntax error example&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you can see on the above screenshot, we display a red square next to the error. We then render the error message (if any) in a proper OpenGL texture and display it at the bottom of the screen.&lt;/p&gt;
&lt;p&gt;Aaaaaand, that&amp;rsquo;s pretty much it. I omitted most of the algorithm describing the editor&amp;rsquo;s behavior, but trust me, there are many. I will probably write another article focusing on this part in the future.&lt;/p&gt;
&lt;p&gt;That was the first time I was writing a text editor from scratch. I can now confirm that this is not, even remotely, a trivial task.&lt;/p&gt;
&lt;p&gt;If you face someday the same problem, let me share with you this advice: find a freaking way to integrate an already existing editor to your program, do not write your own one!&lt;/p&gt;
</description>
    </item>
    <item>
      <title>DobadoBots: Implementing the Parser</title>
      <link>https://alternativebit.fr/posts/dobadobots/dobadoparser/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Thu, 20 Jul 2017 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/dobadobots/dobadoparser/</guid>
      <description>&lt;p&gt;Lately, I have been working on a video-game called &lt;a href=&#34;https://github.com/PicNoir/DobadoBots&#34;&gt;DobadoBots&lt;/a&gt;.
This game is about programming a robot&amp;rsquo;s artificial intelligence to solve mazes.&lt;/p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/Lb8V3ujVHMc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;p&gt;The robot is materialized by a white triangle. The goal is to reach the objective (orange square) while avoiding several obstacles.&lt;/p&gt;
&lt;p&gt;Instead of using a standard embeddable script language such as LUA, I went the custom way and wrote my own language.&lt;/p&gt;
&lt;p&gt;In a previous post, we detailed the AST specification and structure. In this post, we go through the implementation of the parser.&lt;/p&gt;
&lt;p&gt;As a quick reminder, the language we are attempting to parse looks like this&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;IF&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;laserDistance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;moveForward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;ELSE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;IF&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;laserScan&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objective&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;moveForward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;ELSE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kt&#34;&gt;IF&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;laserScan&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obstacle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;n&#34;&gt;turnLeft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kt&#34;&gt;ELSE&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;faceObjectives&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;TL;DR, show me the code instead:&lt;/strong&gt; &lt;a href=&#34;https://github.com/PicNoir/DobadoBots/blob/master/src/DobadoBots/Interpreter/Parser.hs&#34;&gt;here you go&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;parsec&#34;&gt;Parsec&lt;/h2&gt;
&lt;p&gt;As a new Haskell developer, I went through the various available parsing libraries. I finally decided to use the Parsec library.
I have been amazed by it so far. This is probably the best parsing library I ever used.&lt;/p&gt;
&lt;p&gt;The base concept of Parsec is to combine multiple Parser monads in order to create a monad that will either parse your AST, either return a detailed error message.&lt;/p&gt;
&lt;p&gt;Parsec is providing basic Parser monads for the most common builtin datatypes (e.g. space, newline, character, integer, double, etc.). You can then build more useful parsers by assembling those basic ones using some combinators.&lt;/p&gt;
&lt;p&gt;I find this design very elegant, however, after using this library a bit, you can very quickly mess things up. Therefore, I would recommend writing a lot of unit tests before implementing your parser.&lt;/p&gt;
&lt;h2 id=&#34;lets-parse-dobadolang&#34;&gt;Let&amp;rsquo;s Parse Dobadolang&lt;/h2&gt;
&lt;p&gt;There are several ways to use parsec. You can use the monadic approach and take advantage of the do notation or you can use the applicative approach and use the applicative style to combine the various parsers.&lt;/p&gt;
&lt;p&gt;I decided to use the applicative approach for this project as it was a more compact notation.&lt;/p&gt;
&lt;p&gt;We will build our compiler using the bottom up approach, let&amp;rsquo;s start by specifying the AST&amp;rsquo;s leaves: the action token.&lt;/p&gt;
&lt;h3 id=&#34;parsing-action-tokens&#34;&gt;Parsing Action Tokens&lt;/h3&gt;
&lt;p&gt;Each token of our language is defined by a specific string. Parsec provides a &lt;code&gt;string&lt;/code&gt; parser which can parse a simple string case-sensitively. We can, for example, parse the move forward token like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;moveForward&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Alright, but if we look at the type of string, we see we done something wrong.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;Stream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Char&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ParsecT&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Yup, the actual returned value will be the string itself. We do not want that, we want to return the AST element instead.&lt;/p&gt;
&lt;p&gt;We will use the &lt;code&gt;&amp;lt;$&lt;/code&gt; version of fmap. This inflix function basically discards the return value contained in the parsec parser to instead return the value specified at its left.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;MoveForward&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;moveForward&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We use a similar technique to parse the other action tokens.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;actionParser&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CharParser&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ActionToken&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;actionParser&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;MoveForward&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;&amp;lt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;moveForward&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;o&#34;&gt;&amp;lt;|&amp;gt;&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;TurnLeft&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;&amp;lt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;turnLeft&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;o&#34;&gt;&amp;lt;|&amp;gt;&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;TurnRight&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;&amp;lt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;turnRight&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;o&#34;&gt;&amp;lt;|&amp;gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;FaceObjective&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;&amp;lt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;faceObjective&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;o&#34;&gt;&amp;lt;|&amp;gt;&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;ChangeObjective&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;changeObjective&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are two new tricks here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the try function prevents parsers from consuming too many inputs. You can check a detailed explanation of this function &lt;a href=&#34;https://hackage.haskell.org/package/parsec-3.1.11/docs/Text-Parsec-Prim.html#v:try&#34;&gt;on hackage&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;&amp;lt;|&amp;gt;&lt;/code&gt; combinator here materializes a choice. We use it to try parsing the various tokens.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;parsing-asts-nodes&#34;&gt;Parsing AST&amp;rsquo;s Nodes&lt;/h3&gt;
&lt;p&gt;The whole DobadoBots language is built around the conditional structure. Naturally, we want to parse that structure.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;conditionParser&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CharParser&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;conditionParser&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;IF&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;logicExprParser&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;newline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  &lt;span class=&#34;o&#34;&gt;&amp;lt;*&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parseNested&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;newline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  &lt;span class=&#34;o&#34;&gt;&amp;lt;*&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;ELSE&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;newline&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parseNested&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;parseNested&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;actionParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                              &lt;span class=&#34;o&#34;&gt;&amp;lt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;conditionParser&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Writing this function was my &amp;ldquo;aha!&amp;rdquo; moment, this is when I started to fancy parsec.
Here, we just had to combine the previously implemented logic expression and action token parsers.&lt;/p&gt;
&lt;p&gt;This is basically why I love to use parsec with the applicative notation.
The resulting code seems to be written using a DSL while keeping Haskell&amp;rsquo;s strong type checking. It is both highly readable and easy to reuse.&lt;/p&gt;
&lt;p&gt;Now we have defined the various parsec &lt;code&gt;Parser&lt;/code&gt; instances to generate DobadoBots AST, time to wrap-up the parser.&lt;/p&gt;
&lt;h3 id=&#34;wrapping-up-everything&#34;&gt;Wrapping up Everything&lt;/h3&gt;
&lt;p&gt;Once the &lt;code&gt;Parser&lt;/code&gt; monad is defined, we just need to call the &lt;code&gt;parse&lt;/code&gt; function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;parseScript&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Text&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Either&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ParseError&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;parseScript&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parse&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scriptFile&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Error while parsing script: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;unpack&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As shown in this function definition, the &lt;code&gt;parse&lt;/code&gt; function returns either a parsing error, which contains both the error location and a short message describing what should be there in order to have a valid input.&lt;/p&gt;
&lt;h2 id=&#34;testing-the-implementation&#34;&gt;Testing the Implementation&lt;/h2&gt;
&lt;p&gt;As I mentioned in the introduction, it is quite easy to screw things up when parsing. In order to mitigate that, it seems like a good idea to write some test cases up front.&lt;/p&gt;
&lt;p&gt;I used the &lt;a href=&#34;https://hspec.github.io/&#34;&gt;HSpec&lt;/a&gt; unit test task runner together with stack for running the tests. It is a good idea to inline the fixtures in the test file to avoid any filesystem side effect while writing purely functional tests. I used the  &lt;a href=&#34;https://hackage.haskell.org/package/file-embed&#34;&gt;embed file&lt;/a&gt; library to achieve that.&lt;/p&gt;
&lt;p&gt;You can find the complete test case on the &lt;a href=&#34;https://github.com/PicNoir/DobadoBots/blob/master/test/DobadoBots/ParserSpec.hs&#34;&gt;DobadoBots GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Each test case looks basically like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;should parse simple cond =&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;parseScript&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;decodeUtf8&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;embedFile&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;test/fixtures/script/simplecond.script&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shouldBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Right&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;CmpLogicInt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;LaserDistance&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Token&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;TurnLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Token&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;MoveForward&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are inlining a fixture to check the parse result with an expected AST.&lt;/p&gt;
&lt;p&gt;Another testing approach we could implement would be the &lt;a href=&#34;http://teh.id.au/posts/2017/06/07/round-trip-property/index.html&#34;&gt;round trip property&lt;/a&gt; check.&lt;/p&gt;
&lt;p&gt;The goal of this test is to ensure that &lt;code&gt;parse(print(parse(script))) == parse(script)&lt;/code&gt; is always true. Basically, it checks if your parser/pretty printer couple is correct. I did not figure out how to setup stack to both test the library using HSpec (unit tests) and Quickcheck (property-based tests). I will probably figure out how to do that and write a follow-up article in the near future.&lt;/p&gt;
&lt;p&gt;I do not see anything more to add, as you can see, writing a parser using parsec is not that big of a deal. In the next article, we will cover up Dobadobot&amp;rsquo;s text editor.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>DobadoBots: Specifying the Language</title>
      <link>https://alternativebit.fr/posts/dobadobots/dobadolanguage/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Sun, 25 Jun 2017 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/dobadobots/dobadolanguage/</guid>
      <description>&lt;p&gt;Lately, I have been working on a video-game called &lt;a href=&#34;https://github.com/PicNoir/DobadoBots&#34;&gt;DobadoBots&lt;/a&gt;.
This game is about programming a robot&amp;rsquo;s articial intelligence to solve mazes.&lt;/p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/Lb8V3ujVHMc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;p&gt;The robot is materialized by a white triangle. The goal is to reach the objective (orange square) while avoiding several obstacles.&lt;/p&gt;
&lt;p&gt;Instead of using a standard embeddable script language such as LUA, I went the custom way and wrote my own language.&lt;/p&gt;
&lt;h2 id=&#34;specifying-the-language&#34;&gt;Specifying the Language&lt;/h2&gt;
&lt;p&gt;I had only one idea in mind when started to specify this language: keep it as simple as possible. I started wondering, what do we really need to program this kind of artificial intelligence? What is the smallest set of operations the language needs to support?&lt;/p&gt;
&lt;p&gt;I decided to specify the language as a decision tree. Each condition being a node, each robot movement being a leaf.
No loops, no variable assignments, no functions. We do not want to embarrass the player with unnecessary concepts.&lt;/p&gt;
&lt;p&gt;Basically, the language structure looks like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;IF&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sensor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;action&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nested&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;condition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;ELSE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;action&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nested&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;condition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The sensor statement is a boolean expression. It is evaluated in order to decide which subtree should be next executed.&lt;/p&gt;
&lt;p&gt;Every sensor evaluation results either in an action token, which gives the robot a movement instruction, or in a nested condition, which gives us a way to refine the behaviour.&lt;/p&gt;
&lt;p&gt;In the end, a robot&amp;rsquo;s AI will be defined by something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;IF&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;laserDistance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;moveForward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;ELSE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;IF&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;laserScan&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objective&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;moveForward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;ELSE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kt&#34;&gt;IF&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;laserScan&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obstacle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;n&#34;&gt;turnLeft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kt&#34;&gt;ELSE&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;faceObjectives&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;sensors-and-actions&#34;&gt;Sensors and Actions&lt;/h2&gt;
&lt;p&gt;We then needed to determine the minimal operations subset required to control the robot though the maze.&lt;/p&gt;
&lt;p&gt;We finally end up with the following sensors list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;laserDistance&lt;/strong&gt;: checks the distance between the robot and the nearest object.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;laserScan&lt;/strong&gt;: checks what kind of object is facing the robot. It can be an obstacle, a wall, another robot or the objective.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;objectiveDistance&lt;/strong&gt;: checks the distance between the robot and the objective.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ObjectiveDistance was not necessary but makes solutions less confusing in some cases.&lt;/p&gt;
&lt;p&gt;Regarding the actions, we ended up keeping 4 of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;moveForward&lt;/strong&gt;: the robot moves one step ahead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;turnLeft&lt;/strong&gt;: the robot rotates counterclockwise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;turnRight&lt;/strong&gt;: the robot rotates clockwise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;faceObjective&lt;/strong&gt;: the robot faces the objective.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;implementing-the-abstract-syntax-tree-ast&#34;&gt;Implementing the Abstract Syntax Tree (AST)&lt;/h2&gt;
&lt;p&gt;Now we specified the language, we still need to implement its AST. We will use the Haskell programming language for this part.&lt;/p&gt;
&lt;p&gt;We start specifying data structures for both actions and sensors tokens. This problem is quite straightforward, we just need to use Haskell&amp;rsquo;s algebraic data types.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ActionToken&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;MoveForward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;TurnLeft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;TurnRight&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;FaceObjective&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ChangeObjective&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;kr&#34;&gt;deriving&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SensorToken&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;LaserDistance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;LaserScan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ObjectiveDistance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;kr&#34;&gt;deriving&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We then need to specify the logic expression data type. We need to keep in mind that comparing a laser distance with an integer is quite different from comparing a laserScan with a Collider.&lt;/p&gt;
&lt;p&gt;In order to simplify both the parser and the interpreter, we restricted as much as possible the type definition for both of these kind of logic expression.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;LogicExpr&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CmpCollider&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SensorToken&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Collider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CmpLogicInt&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;CmpInteger&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;SensorToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               &lt;span class=&#34;kr&#34;&gt;deriving&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CmpInteger&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Sup&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Integer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Inf&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Integer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Integer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;kr&#34;&gt;deriving&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Collider&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Obstacle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Objective&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Wall&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Robot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;kr&#34;&gt;deriving&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we have implemented all the basic data types, we just need to compose them in order to build the actual tree.&lt;/p&gt;
&lt;p&gt;To do so, we use a recursive data type.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Token&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ActionToken&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sensor&lt;/span&gt;    &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;LogicExpr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ifValid&lt;/span&gt;   &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ifInvalid&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Cond&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;deriving&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We now successfully implemented our AST, &lt;code&gt;Cond&lt;/code&gt; being its root.&lt;/p&gt;
&lt;p&gt;In next article, we will dive in the associated parser&amp;rsquo;s implementation.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>One Year of FOSS</title>
      <link>https://alternativebit.fr/posts/sabatical/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Sun, 18 Jun 2017 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/sabatical/</guid>
      <description>&lt;h1 id=&#34;one-year-of-foss&#34;&gt;One Year of FOSS&lt;/h1&gt;
&lt;p&gt;Hello internet friends, long time no see.&lt;/p&gt;
&lt;p&gt;After doing a total revamp of this weblog, I think it is finally time to explain what&amp;rsquo;s happening in my life.&lt;/p&gt;
&lt;p&gt;Two months ago, I decided to quit my job. I was tired of Paris&amp;rsquo;s pollution. I was tired of Paris&amp;rsquo;s shitty public transportation. I was not really happy doing my job. I had one year of runway. Long story short, I moved to &lt;a href=&#34;https://en.wikipedia.org/wiki/Bayonne&#34;&gt;Bayonne&lt;/a&gt;, a really nice place in southern France.&lt;/p&gt;
&lt;p&gt;Sadly, as nice as this place is, we cannot call it a tech-friendly place. There is not a lot of software-related jobs there, even less interesting ones. So instead of looking for a new job, I decided to do something bold. Why not, instead of constantly whining about low quality software, trying to write something I can be proud of, software I personally enjoy, and in the meantime, forgetting about getting any income for one year?&lt;/p&gt;
&lt;h2 id=&#34;the-manifesto&#34;&gt;The Manifesto&lt;/h2&gt;
&lt;p&gt;I came up with this manifesto:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do not think about money or employment, only think about learning and creating something useful.&lt;/li&gt;
&lt;li&gt;Each started project will be a completed one. Even if it turns out to be a failure or a bad idea, reach at least the minimal viable product stade. &lt;strong&gt;There is no escape&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Do not parallelize projects. You should have one and only one software
project at a time.&lt;/li&gt;
&lt;li&gt;Release everything you do in an opensource form. Preferably using the MIT V3 license.&lt;/li&gt;
&lt;li&gt;Try to cooperate with other people as much as possible in everything you do.&lt;/li&gt;
&lt;li&gt;Share your experiment publicly in an honest way. No idealistic bullshit, if something you do sucks, say it and explain why.&lt;/li&gt;
&lt;li&gt;Try to suck as less at possible in your job. You are here to learn about how to write highly reliable software, regardless how long it takes. &lt;strong&gt;Time will not be a constraint this year&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I have been following these rules for the last two months.&lt;/p&gt;
&lt;h2 id=&#34;what-have-i-done-so-far&#34;&gt;What Have I Done so Far?&lt;/h2&gt;
&lt;p&gt;I first started learning haskell. Yes, yet another time. This was the third time I was attempting to learn that damn language. I can finally say that this was definetely the last try. I grasped enough concepts to have a real world usage of it. It took me one month: the first one of my sabbatical.&lt;/p&gt;
&lt;p&gt;This time, instead of trying to get started in two days using some crappy internet resources, I went the hard way. I studied the &lt;a href=&#34;http://book.realworldhaskell.org/read/&#34;&gt;real world haskell&lt;/a&gt; book.
I couldn&amp;rsquo;t recommand it enough, it explores the language in depth with enough practical exercises and examples to grasp the presented concepts. As I was mentionning, it took me around 3 weeks fulltime to finish that book. So be warned, Haskell is a great language, but do not expect to grasp the basics in 2 days as you would do with yet another c-based language.&lt;/p&gt;
&lt;p&gt;After getting through this book, I decided to start right away a Haskell project from scratch, just to prove myself I was able to build something practical with this language.&lt;/p&gt;
&lt;p&gt;This is when I decided to create a videogame. Partly because it was a child&amp;rsquo;s dream, partly because I was curious about how to build a purely functional game.&lt;/p&gt;
&lt;p&gt;I will write more in-depth about DobadoBots in some upcoming articles, but long story short, after one month of development, I am yak-shaving its minimal version. All the features are now implemented, it only needs some polishing.&lt;/p&gt;
&lt;h2 id=&#34;radical-lifestyle-change&#34;&gt;Radical Lifestyle Change&lt;/h2&gt;
&lt;p&gt;Starting a new life phase is always a hustle. This time was no different.&lt;/p&gt;
&lt;p&gt;First, the city. Moving from a 2 million inhabitants metropolis to a small 15 000 inhabitants city was a hell of a change. In the end, I couldn&amp;rsquo;t be more happy. The city center is pedestrians only: no more pollution nor car noise all day long. Everything is within reach using a bycicle: no more crowded public transportations. I can reach the beach in a 20 minutes bycicle ride. I really like this place, I actually would love to stay here even after this sabbatical.&lt;/p&gt;
&lt;p&gt;Next, work. I am not going to lie, this has been the harder part. Moving from a on site white-collar job to a completely constraint-free homeschooled one has been greatly affecting my show up rate! During the first month, I have been constantly struggling with self-motivation. Not having external constraints and sharing the same space for both personal life and work turned out to be encouraging me procrastinating. Luckily, I finally catched up momentum and have been effectively working fulltime for the past month. I guess this struggle will never end, but at least I now know that I can effectively beat procrastination!&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s pretty much it so far, I think I will try to write an update about this sabbatical after every project wrapup.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Real World Haskell Chapter 9 Solutions</title>
      <link>https://alternativebit.fr/posts/real-word-haskell-chapter-9/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Tue, 04 Apr 2017 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/real-word-haskell-chapter-9/</guid>
      <description>&lt;h1 id=&#34;exercise-p221&#34;&gt;Exercise P.221&lt;/h1&gt;
&lt;p&gt;Is the order in which we call bracket and handle important?&lt;/p&gt;
&lt;p&gt;Yup, it is pretty important: the code executed during the in-between statement of bracket still can raise
an exception. We do not want our application to crash in case of an error while opening the file, therefore, we
still need the handle statement.&lt;/p&gt;
&lt;p&gt;We could put the handle statement inside of the bracket statement, but since no exception would be thrown,
we would risk to leak some resources.&lt;/p&gt;
&lt;h1 id=&#34;exercise-p228&#34;&gt;Exercise P.228&lt;/h1&gt;
&lt;h2 id=&#34;question-1&#34;&gt;Question 1&lt;/h2&gt;
&lt;p&gt;How could we traverse the directory three in reverse alphabetic order?&lt;/p&gt;
&lt;p&gt;Well, it is quite straightforward, before iterating on directories, we apply the order
function on the list containing them. Therefore, we only need to apply
&lt;a href=&#34;https://hackage.haskell.org/package/base-4.9.1.0/docs/Prelude.html#v:reverse&#34;&gt;reverse&lt;/a&gt; as the order function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;ControllerVisit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ControllerVisit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;traverse&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reverse&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/home/minoulefou/test&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;question-2&#34;&gt;Question 2&lt;/h2&gt;
&lt;p&gt;Implement an order function which traverses the tree in postorder.&lt;/p&gt;
&lt;p&gt;For each file info type handled by the &lt;code&gt;order&lt;/code&gt; function, we have two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The file is a directory&lt;/strong&gt;: we want to first handle its children then
handle the directory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The file is not a directory&lt;/strong&gt;: we will apply the identity function to this
file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I implemented this recursive function as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;postOrder&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;postOrder&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;isDirectory&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;then&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;postOrder&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;postOrder&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;postOrder&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it the implementation is quite straightforward.&lt;/p&gt;
&lt;p&gt;There may be some other ones, do not hesitate to shout me out if you found
something else.&lt;/p&gt;
&lt;h2 id=&#34;question-3&#34;&gt;Question 3&lt;/h2&gt;
&lt;p&gt;We want to adapt previously implemented predicates with the new &lt;code&gt;Info&lt;/code&gt; datatype.&lt;/p&gt;
&lt;p&gt;First we need to express InfoP using info:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Info&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We will now implement predicates equality and comparaison: we&amp;rsquo;ll need to
reimplement liftP with the new InfoP type:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;liftP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;liftP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;op&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;infoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;infoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;op&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;test&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once we implemented this function, implementing equality and inequity predicates
is trivial:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;greaterP&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lesserP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Ord&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;greaterP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;liftP&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;lesserP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;liftP&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;equalsP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;equalsP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;liftP&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once again, in order to implement combinators, we&amp;rsquo;ll use another lift
function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;liftP2&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;liftP2&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;op&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred2&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;op&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred2&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;andP&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;orP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;andP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;liftP2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;orP&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;liftP2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As for the previous question, we&amp;rsquo;ll use some infix notations for each operation:
it is more handy to use.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Eq&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;equalsP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Ord&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;greaterP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lesserP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;InfoP&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;andP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;orP&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;question-4&#34;&gt;Question 4&lt;/h2&gt;
&lt;p&gt;Write a wrapper for &lt;code&gt;traverse&lt;/code&gt; that lets you control traversal using one
predicate and filter results using another.&lt;/p&gt;
&lt;p&gt;Here, we want to add a behaviour to the ord function. Not only we want that
function to order the results, be we also want it to filter them. An easy
approach to this problem seems to be composing the filter function with the
order function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;traverse&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ControllerVisit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;traverse&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;order&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&#34;exercise-p-232&#34;&gt;Exercise P. 232&lt;/h1&gt;
&lt;h2 id=&#34;question-1-1&#34;&gt;Question 1&lt;/h2&gt;
&lt;p&gt;Modify foldtree to allow the caller to change the order of traversal of entries
in a directory.&lt;/p&gt;
&lt;p&gt;First, we need to specify the new fold function signature:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;foldTree&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;FilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;FilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Iterator&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;FilePath&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;foldTree&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;initSeed&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We will plug this function to the existing foldTree on the fold subfunction as follow:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;fold&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seed&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subpath&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;paths&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getUsefulContent&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subpath&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;walk&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seed&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;paths&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The remaining code will still unchanged.&lt;/p&gt;
&lt;p&gt;Aaaand, the two remaining exercises are quite similar to the previous ones, I
will pass on that :)&lt;/p&gt;
&lt;p&gt;See you soon for chapter 10 solutions!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/memes/bye-tree.gif&#34; alt=&#34;Bye Bye&#34;&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Real World Haskell Chapter 8 Solutions</title>
      <link>https://alternativebit.fr/posts/real-word-haskell-chapter-8-efficient-file-processing/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Tue, 28 Mar 2017 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/real-word-haskell-chapter-8-efficient-file-processing/</guid>
      <description>&lt;p&gt;Hello, it&amp;rsquo;s been a long time.&lt;/p&gt;
&lt;p&gt;Despite having done some progress on the resolution of the exercises of this book, I haven&amp;rsquo;t blogged about my solutions, which is a shame. The solutions are available in this
&lt;a href=&#34;https://github.com/PicNoir/Real-World-Haskell&#34;&gt;git repository&lt;/a&gt; though.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start again for the end of chapter 8.&lt;/p&gt;
&lt;h1 id=&#34;exercises-p205&#34;&gt;Exercises P.205&lt;/h1&gt;
&lt;p&gt;Here, we need to implement a case insensitive version of the glob checker.&lt;/p&gt;
&lt;p&gt;Despite having looked on Hoogle for some elegant solution, I did not found anything suitable… I ended up writing
a dirty hack.&lt;/p&gt;
&lt;p&gt;The idea is to add a boolean flag to the matches glob. If this boolean is unset - meaning that we do not want to be
case sensitive - we uppercase both the filename and the pattern.&lt;/p&gt;
&lt;p&gt;Despite not being an elegant solution, it works.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;matchesGlob&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;FilePath&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;matchesGlob&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pat&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;globToRegex&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;matchesGlob&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pat&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;False&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;toUpper&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;globToRegex&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;toUpper&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&#34;exercises-p210&#34;&gt;Exercises P.210&lt;/h1&gt;
&lt;h2 id=&#34;generalisation-of-the-case-sensitivity&#34;&gt;Generalisation of the case sensitivity&lt;/h2&gt;
&lt;p&gt;First of all, we need a way to determine that we are on a windows system. According to &lt;a href=&#34;https://hackage.haskell.org/package/filepath-1.4.1.2/docs/System-FilePath-Windows.html#v:isPathSeparator&#34;&gt;Hoogle&lt;/a&gt;,
the isPathSeparator function seems to be a pretty good candidate. If &lt;code&gt;&#39;\&#39;&lt;/code&gt; is a path separator, then we are on a windows environment.&lt;/p&gt;
&lt;p&gt;So first, let&amp;rsquo;s select the right glob matching func:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;globMatchingFunc&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;isPathSeparator&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;#39;\\&lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                       &lt;span class=&#34;kr&#34;&gt;then&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;matchesGlobCaseSensiitive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                       &lt;span class=&#34;kr&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;matchesGlobNotCaseSensitive&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we&amp;rsquo;ll use this function in the filter part:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;globMatchingFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;names&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find the entire file in order to have a higher level-view &lt;a href=&#34;https://github.com/PicNoir/Real-World-Haskell/blob/master/ch8/Glob.hs&#34;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;replacement-for-doesnameexist-on-posix-systems&#34;&gt;Replacement for doesNameExist on POSIX systems&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://hackage.haskell.org/package/unix-2.7.2.1/docs/System-Posix-Files.html#v:fileExist&#34;&gt;fileExists&lt;/a&gt;
would be a good replacement for doesNameExists.&lt;/p&gt;
&lt;p&gt;We could use that function because everything can be seen as a file on a POSIX system.&lt;/p&gt;
&lt;p&gt;However, using this specificity would make our software POSIX specific thus impossible to run on a windows system.&lt;/p&gt;
&lt;h2 id=&#34;recursive-wild-card-implementation&#34;&gt;Recursive wild card implementation&lt;/h2&gt;
&lt;p&gt;In order to implement this feature, we need several parts. The first one will be a function that returns the childrens
of a directory.&lt;/p&gt;
&lt;p&gt;This feature has been implemented using the following algorithm:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getChildrenDirs&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getChildrenDirs&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getChildrenDirs&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getChildrenDirs&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;getChildrenDirs&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;paths&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;pathNames&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forM&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;paths&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;dirContent&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listDirectory&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dropTrailingPathSeparator&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;absDirContent&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dropTrailingPathSeparator&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;/&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirContent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;dirs&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filterM&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;doesDirectoryExist&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;absDirContent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;then&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dropTrailingPathSeparator&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;children&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getChildrenDirs&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;concat&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pathNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nothing too fancy here, just a recursive tree traversal. The only new thing (at least for me at this point) was the use of filterM which
let us use filter on a IO() list.&lt;/p&gt;
&lt;p&gt;Now we have this function, we can:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect the &lt;code&gt;**&lt;/code&gt; pattern somewhere in the pattern.&lt;/li&gt;
&lt;li&gt;List the subdirectories according the beginning of the path given in parameter.&lt;/li&gt;
&lt;li&gt;Pattern match the files contained in these folders according the basename filter.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to implement point 1, we&amp;rsquo;ll need to implement a simple predicate detecting the &amp;ldquo;double wildcard&amp;rdquo;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;containsDoubleWildcard&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;FilePath&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;containsDoubleWildcard&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;^.*&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\\&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\\&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;*.*$&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we detect this kind of pattern, we want to add the current dir and its children to the file lookup. In order to do that,
we will add these directories to the &amp;ldquo;dirs&amp;rdquo; list in the namesMatching as follow:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;dirs&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;isPattern&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;isPattern&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;baseName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;then&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;containsDoubleWildcard&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;baseName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;kr&#34;&gt;then&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getChildrenDirs&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;kr&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;namesMatching&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dropTrailingPathSeparator&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;kr&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The rest of the namesMatching will take care of the pattern matching on the files contained in the dirs list.&lt;/p&gt;
&lt;p&gt;You can find the complete solution to the exercise on &lt;a href=&#34;https://github.com/PicNoir/Real-World-Haskell/blob/master/ch8/Glob.hs&#34;&gt;this git repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The last question being trivial, we are done with this chapter :)
&lt;img src=&#34;https://alternativebit.fr/images/memes/rmrf.gif&#34; alt=&#34;rmrf all&#34;&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Real World Haskell Chapter 4 Solutions</title>
      <link>https://alternativebit.fr/posts/real-word-haskell-chapter-4-functionnal-programming/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/haskell">Haskell</category>
      <pubDate>Sun, 18 Dec 2016 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/real-word-haskell-chapter-4-functionnal-programming/</guid>
      <description>&lt;p&gt;Okay, in this chapter, we will apparently learn more about common techniques in FP, can&amp;rsquo;t wait!&lt;/p&gt;
&lt;h1 id=&#34;exercises-page-84&#34;&gt;Exercises page 84&lt;/h1&gt;
&lt;h2 id=&#34;lets-rewrite-safe-versions-of-partial-list-functions&#34;&gt;Let&amp;rsquo;s rewrite safe versions of partial list functions&lt;/h2&gt;
&lt;p&gt;The first two functions are very straightforward: a bit of pattern matching,
some identification of edge cases and we&amp;rsquo;re done.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeHead&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Maybe&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeHead&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Nothing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeHead&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Just&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeTail&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Maybe&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeTail&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Nothing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeTail&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Just&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeLast&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Maybe&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeLast&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Nothing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeLast&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Just&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeLast&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;safeLast&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It gets a bit more tricky for the last one. Indeed, we need to recursively construct a list from the end to the beginning
and wrap everything in a Maybe. In order to do that, I first calculate the list in a sub-function defined in a where section
and then encapsulate everything in the Maybe.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeInit&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Maybe&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeInit&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Nothing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;safeInit&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Just&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;splitwith-an-inverted-word&#34;&gt;SplitWith: an inverted word&lt;/h2&gt;
&lt;p&gt;This definition is so confusing… I am not sure about getting the specification right, but from what I understand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have a list we want to split in sublists.&lt;/li&gt;
&lt;li&gt;We need to evaluate an expression for each list member.&lt;/li&gt;
&lt;li&gt;If this evaluation is false, we need to split the main list at this point.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;splitWith&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;splitWith&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;splitWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;takeWhile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;splitWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;remaining&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;endOfList&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dropWhile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;remaining&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;endOfList&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;otherwise&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tail&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endOfList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First, let&amp;rsquo;s think about edge cases. Obviously, the split of an empty list will results to an empty list.&lt;/p&gt;
&lt;p&gt;Then, we need to write a recursive pass on the list. During this pass, we will first use the takeWhile function in order to take elements that verify the function. Then we will append these elements
to the rest of the list minus the &amp;ldquo;delimiter&amp;rdquo;. I insist, you need to remove this delimiter, otherwise you&amp;rsquo;ll end up with an infinite loop.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/memes/truestory.png&#34; alt=&#34;True Story Bro&amp;rsquo;&#34;&gt;&lt;/p&gt;
&lt;p&gt;In order to calculate the rest of the list (list minus delimiter), I used the function tail. As we seen in the last exercise, this function is quite unsafe. We need to be extra careful
that we are not tailing an empty list.&lt;/p&gt;
&lt;p&gt;Again, I am not sure that I got the specification right in the first place.&lt;/p&gt;
&lt;h2 id=&#34;printing-first-word-for-each-input-line&#34;&gt;Printing first word for each input line&lt;/h2&gt;
&lt;p&gt;This exercise is quite simple, we want to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read a file.&lt;/li&gt;
&lt;li&gt;Take the first character of each line.&lt;/li&gt;
&lt;li&gt;Print it to another file.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- We can reuse this code which is given in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- the previous chapter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;interactWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;inputFile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;outputfile&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readFile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;inputFile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;writeFile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;outputfile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mainWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;myFunction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;myFunction&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getFirstWordsOfFile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    	      &lt;span class=&#34;n&#34;&gt;mainWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    	      &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;getArgs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    	      &lt;span class=&#34;kr&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        	      &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;interactWith&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	       	      &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;putStrLn&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;error: exactly two arguments needed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;firstWord&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;firstWord&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;words&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;firstWordsOfFile&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;firstWordsOfFile&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;firstWordsOfFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstWord&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstWordsOfFile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getFirstWordsOfFile&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;getFirstWordsOfFile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileStr&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;firstWordsOfFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you see, this is quite simple, there are 3 functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;firstWord&lt;/strong&gt;: which chops a line and returns the first word.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;firstWordsOfFile&lt;/strong&gt;: which takes the lines of the file one by one and returns only the first words.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;getFirstWordsOfFile&lt;/strong&gt;: which takes the file string, chop it line by line and returns the first word.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I could have used where or let in order to encapsulate the intermediates functions. But I haven&amp;rsquo;t…&lt;/p&gt;
&lt;h2 id=&#34;transposing-text-from-a-file&#34;&gt;Transposing text from a file&lt;/h2&gt;
&lt;p&gt;Aaaaaaand, dunno. Seriously, I do not get how to do that without the map function… Shout me out if you found the solution!&lt;/p&gt;
&lt;h1 id=&#34;exercises-page-98&#34;&gt;Exercises page 98&lt;/h1&gt;
&lt;h2 id=&#34;rewriting-asint-using-folds&#34;&gt;Rewriting asInt using folds&lt;/h2&gt;
&lt;p&gt;This one is easy, we just fold from the left the char expression and we keep
the integer representation up to date by multiplying by 10.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Data.Char&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;digitToInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldl&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Char&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;digitToInt&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;h2 id=&#34;handling-negative-numbers&#34;&gt;Handling negative numbers&lt;/h2&gt;
&lt;p&gt;Okay, now we want to handle negative numbers.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldl&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Char&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;digitToInt&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
In order to do that, we pattern match the head of the string and we filter the &lt;strong&gt;-&lt;/strong&gt;
using guards.&lt;/p&gt;
&lt;h2 id=&#34;handling-common-errors&#34;&gt;Handling common errors&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s add some conditions in order to handle common errors.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Cannot convert [] to Int&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;  &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Cannot convert this expression to Int.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_fold&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldl&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Char&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;digitToInt&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Please note that I did not found any direct way to correctly handle Int overflow other than using the safeint type (which I did not implemented here).&lt;/p&gt;
&lt;h2 id=&#34;safe-error-handling-using-either&#34;&gt;Safe error handling using either&lt;/h2&gt;
&lt;p&gt;Here, we&amp;rsquo;re going to wrap up our error handling in the either
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ErrorMessage&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_either&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Either&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ErrorMessage&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_either&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Left&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Cannot convert [] to Int&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_either&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;  &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Left&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Cannot convert this expression to Int.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fmap&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;asInt_either&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;asInt_either&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Right&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;foldl&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Char&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;digitToInt&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
I did not found a way to solve this problem without using fmap which has not been introduced yet.
Maybe I missed something here or maybe this is an error in the book…&lt;/p&gt;
&lt;h2 id=&#34;implementing-concat-using-foldr&#34;&gt;Implementing concat using foldr&lt;/h2&gt;
&lt;p&gt;Nothing to say here, this is straightforward
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;concat_foldr&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;concat_foldr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;h2 id=&#34;implementing-takewhile-using-foldr&#34;&gt;Implementing takewhile using foldr&lt;/h2&gt;
&lt;p&gt;Again, nothing special here.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;takeWhile&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;takeWhile&amp;#39;&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;takeWhile&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;takeWhile&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;otherwise&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;takeWhileFold&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;takeWhileFold&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;otherwise&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;h2 id=&#34;implementing-lists-groupby&#34;&gt;Implementing List&amp;rsquo;s groupBy&lt;/h2&gt;
&lt;p&gt;After playing a bit with ghci, it turns out that group by is taking a 2 parameters function and a list and groups the list&amp;rsquo;s elements using the function given in parameters and the first element.
As soon as it is impossible to group with the first element, a new group is created and so on.
For instance:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Main&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;groupBy&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Okay, let&amp;rsquo;s implement that.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-hs&#34; data-lang=&#34;hs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;groupBy&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;groupBy&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldl&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;foldFunc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;otherwise&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;elem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
This problem turned out to be very tricky. For each element, we want to check if we want to pair it with the current subgroup.&lt;/p&gt;
&lt;p&gt;In order to do that, we need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the current subgroup using last acc.&lt;/li&gt;
&lt;li&gt;Get the first element of this subgroup which is the group &amp;ldquo;master&amp;rdquo; using head(subgroup)&lt;/li&gt;
&lt;li&gt;Check if pred is true with the &amp;ldquo;master&amp;rdquo; and the current elem.
&lt;ul&gt;
&lt;li&gt;If it is, we need to append elem to the current subgroup.&lt;/li&gt;
&lt;li&gt;Otherwise, we create a new subgroup with elem as the &amp;ldquo;master&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As you can see, the code is really messy. I think that I should have created a specific datatype for the subgroup,
it could have made pattern matching easier…&lt;/p&gt;
&lt;p&gt;Aaaaand that&amp;rsquo;s it!!! We done it!!! Time for celebration now!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://alternativebit.fr/images/memes/cute_party.gif&#34; alt=&#34;Cute dancing monster&#34;&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Open recipe database: how to gather, cure and store data</title>
      <link>https://alternativebit.fr/posts/open-receipe-database-how-to-gather-and-cure-data/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/weblog">Web log</category>
      <pubDate>Fri, 08 Jul 2016 20:29:54 +0200</pubDate>
      <guid>https://alternativebit.fr/posts/open-receipe-database-how-to-gather-and-cure-data/</guid>
      <description>&lt;p&gt;I recently started to think about creating an open recipe database. The major consideration coming with this project is how to gather, cure and store recipes.&lt;/p&gt;
&lt;h1 id=&#34;gathering-data&#34;&gt;Gathering data&lt;/h1&gt;
&lt;p&gt;Let&amp;rsquo;s start with something obvious: we cannot rely on users to create recipes from scratch.
This is a very time consuming task, so the first approach would be to find a way to fully automate that.&lt;/p&gt;
&lt;p&gt;As I mentioned in the previous article, the main goal of this project is to keep the recipes informations in a highly structured way.
Even if we can easily scrap recipes from the internet, it is usually hard to analyse a full text format in order to fit it
in a very structured and restricted database. It would involve some advanced machine learning techniques which I do not
master at all and will highly raise this project&amp;rsquo;s complexity.&lt;/p&gt;
&lt;p&gt;In order to keep things simple, we need to find a way to let users cure the unstructured information for us.&lt;/p&gt;
&lt;p&gt;We can find loosely structured recipes databases all around the internet. They usually come under two flavours:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Websites formatting their HTML using &lt;a href=&#34;http://microformats.org/wiki/hrecipe&#34;&gt;hrecipe&lt;/a&gt;. It basically use html classes
to add some semantic informations and metadatas about recipes. Ok, it is more a way to add semantical informations on
a website than a database per say, but it makes scrapping these website easier.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://open-recipe-format.readthedocs.io/en/latest/&#34;&gt;Open Recipes Format&lt;/a&gt; based database. &lt;a href=&#34;https://github.com/fictivekin/openrecipes&#34;&gt;Fictivekin&amp;rsquo;s Open Recipes&lt;/a&gt;
seems to be the more complete one even though it is more intended to be used as a recipe bookmark as mentioned in the repository. This format is YAML based.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Neither of these formats are strict enough for this project but it will be a good starting point which would need to be human curated.&lt;/p&gt;
&lt;h1 id=&#34;storing-data&#34;&gt;Storing data&lt;/h1&gt;
&lt;p&gt;We will not go in depth here, we will just identify the various entities. This list is not an exhaustive one and there are obviously lot of dependencies between these
entities, they will not be detailed in this post.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Recipe:&lt;/strong&gt; obviously&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingredient:&lt;/strong&gt; it will contain one ingredient. Each ingredient should be unique in the database, we should avoid as much as possible duplicates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nutrition information:&lt;/strong&gt; it will contain various nutritive informations. It will be associated with the ingredient entity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diet information:&lt;/strong&gt; is this vegan/kasher/halal/vegetarian/watever friendly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technique:&lt;/strong&gt; cooking technique, such as grill, peel, cut, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cooking step:&lt;/strong&gt; Combination of a technique + several ingredients.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There will also be &amp;ldquo;links&amp;rdquo; between these entities, for the moment, I just identified one of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Substitutes:&lt;/strong&gt; link between two ingredients. This link will probably also contain informations such as weight/volume ratio, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;curating-data&#34;&gt;Curating data&lt;/h1&gt;
&lt;p&gt;Now we have some data, we need to cure it in order to make it fit in our strict table scheme. The idea here is to let users do that, a bit like reddit
let users cure internet links.&lt;/p&gt;
&lt;p&gt;We will need to design a proper algorithm here and I will not get into that until another post, however, the outline will be the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each entity (ingredient, recipe, …) will be associated to three states:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;New:&lt;/strong&gt; a user is editing the entity, it is not ready for vote.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Voting:&lt;/strong&gt; the entity is currently candidate for integration in the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rejectted:&lt;/strong&gt; the entity has been rejected, it will not be inserted in the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integrated:&lt;/strong&gt; the entity is integrated in the database.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Every user is associated to a rank (karma) which will weight the votes.&lt;/li&gt;
&lt;li&gt;During the &lt;strong&gt;voting&lt;/strong&gt; state, users are invited to upvote or downvote the entity.&lt;/li&gt;
&lt;li&gt;When an entity score meets a threshold (1 or -1), it will be accepted or rejected.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;tricky-parts&#34;&gt;Tricky parts&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Every&lt;/strong&gt; entity will need to be voted in order to be integrated in the database. This will probably prevent us to integrate duplicates and crappy data. However,
this may also discourage users from posting as they will not know if their work will be integrated or not.&lt;/p&gt;
&lt;p&gt;Another problem will be entity dependencies: recipes will depend on ingredients, it means that in order to be included in the database, a recipe needs all its
ingredients being already included. What if some ingredient are rejected, how should we handle that? I need to put more thoughs on that later…&lt;/p&gt;
&lt;p&gt;Do we need moderators? I would prefer not in order to keep the database as neutral as possible, but depending of users activeness, it could be needed.&lt;/p&gt;
&lt;h1 id=&#34;coming-next&#34;&gt;Coming next&lt;/h1&gt;
&lt;p&gt;Alright, enough talk, we need to start implementing that.&lt;/p&gt;
&lt;p&gt;Next step: entity system implementation.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Some thoughts about building an open recipe database</title>
      <link>https://alternativebit.fr/posts/some-thoughts-about-an-open-receipes-database/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/weblog">Web log</category>
      <pubDate>Thu, 23 Jun 2016 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/some-thoughts-about-an-open-receipes-database/</guid>
      <description>&lt;p&gt;Lately, I have been thinking about creating an open recipe database. It is just not
possible to find any good quality recipes database. Often, the website indexing
recipes does not expose its data using any kind of API. When it does - and very
few does - the recipes are stored in a data format close to unrestricted plain text.&lt;/p&gt;
&lt;h1 id=&#34;advanced-queries&#34;&gt;Advanced queries&lt;/h1&gt;
&lt;p&gt;Most recipes websites are offering very primitives search possibilities. For instance,
&lt;a href=&#34;http://www.marmiton.org/&#34;&gt;Marmiton&lt;/a&gt;, the most complete french recipes database offers
the following search options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meal type&lt;/li&gt;
&lt;li&gt;Difficulty&lt;/li&gt;
&lt;li&gt;Cost - using cheap, average and expensive options&lt;/li&gt;
&lt;li&gt;Vegetarian friendly&lt;/li&gt;
&lt;li&gt;Keywords&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, this search is really primitive, I personally need some more criteria.&lt;/p&gt;
&lt;h2 id=&#34;disclaimer&#34;&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;There is a notable exception: &lt;a href=&#34;http://bigoven.com/&#34;&gt;Big Oven&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This website provides a lot ofrecipes, the ux and search function are really sweet and
all the data is accessible using a REST API.&lt;/p&gt;
&lt;p&gt;The problem with that website, besides being quite buggy (I have not managed to get my API key because of a login related bug…)
is the very restrictive &lt;a href=&#34;http://api2.bigoven.com/web/documentation/terms-of-use&#34;&gt;data license&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Basically, you cannot use this data for anything else than sending users to their website.&lt;/p&gt;
&lt;h1 id=&#34;features-we-need&#34;&gt;Features we need&lt;/h1&gt;
&lt;p&gt;After putting some thoughts on these criteria, I have come up with that list of features
of the ideal recipes web index:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ingredients list:&lt;/strong&gt; I want to be able to specify a ingredients list in which the recipe
will be created on. I also want to exclude some ingredients from the search results because
my diet does not permit to eat these ingredients, because I do not like this ingredient, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Equivalence class between ingredients:&lt;/strong&gt; I want to be able to know if soy creme is an
acceptable substitute to crême fraiche. In a more general way, I want to be able to
determine the substitutes and their quantity substitution for each ingredient.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hierarchy/Relationships/Categories of ingredients:&lt;/strong&gt; Is my meal balanced? Is my meal free of meat?
Is my meal kasher/halal? Does my meal contains fish? What kind of rice should I use for this
particular meal?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measurement scales equivalences:&lt;/strong&gt; what the fuck is a cup? I want grams!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So yeah, seems like a lot of work to do…&lt;/p&gt;
&lt;h1 id=&#34;problem-we-are-actually-facing&#34;&gt;Problem we are actually facing&lt;/h1&gt;
&lt;p&gt;It seems that every recipe index website is getting killed by a new one which is more user
friendly or has more feature every 5 years.&lt;/p&gt;
&lt;p&gt;There is no open database of recipes. Every time a new website is created, the data needs to be
scraped or created from scratch. We need to separate the data (actual recipes) from the
processing/presentational part or we will keep creating some crappy database from scratch every
5 years.&lt;/p&gt;
&lt;p&gt;I think it is time to create something more durable: an &lt;strong&gt;open recipe database&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;More article about this index are about to come.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>How to configure TLS Let&#39;s Encrypt Certificates with Nginx</title>
      <link>https://alternativebit.fr/posts/lets-encrypt-nginx-certonly-autommatized-config/</link>
      <author>PicNoir</author>
      <category domain="https://alternativebit.fr/tags/ops">Ops</category>
      <pubDate>Fri, 04 Dec 2015 00:00:00 +0000</pubDate>
      <guid>https://alternativebit.fr/posts/lets-encrypt-nginx-certonly-autommatized-config/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s encrypt beta is public since yesterday. I already use their certificates for several
domains. The main problem I actually face is certificates renewal. I want to be able to renew
automatically my certificates without turning off Nginx.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s encrypt official client can be used in a automatic way. Unfortunately, this feature is poorly
documented… In order to automate this process, we need to configure Nginx to deliver acme&amp;rsquo;s challenge
specific files, then we need to create a zsh script that we will launch using a Cron routine.&lt;/p&gt;
&lt;h1 id=&#34;nginx-setup&#34;&gt;Nginx Setup&lt;/h1&gt;
&lt;p&gt;If you are already using https on your webserver, you probably have a configuration like this one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    server &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        listen         80&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        server_name    ...&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;         &lt;span class=&#34;m&#34;&gt;301&lt;/span&gt; https://&lt;span class=&#34;nv&#34;&gt;$server_name$request_uri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    server &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        listen &lt;span class=&#34;m&#34;&gt;443&lt;/span&gt; ssl&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ssl_certificate fullchain.pem&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ssl_certificate_key privkey.pem&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        server_name ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        root ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        location / &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          index index.html index.htm index.php&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You are basically redirecting all the http requests to the https endpoint and serving this endpoint
with whatever CGI service you want.&lt;/p&gt;
&lt;p&gt;What we need to do is simply add the &lt;code&gt;/.well-known/acme-challenge&lt;/code&gt; endpoint for each domain. This
endpoint is used by the ACME protocol in order to make sure that we truly own the domain.&lt;/p&gt;
&lt;p&gt;Just alter the http server like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    server &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        listen         80&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        server_name    ...&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;         &lt;span class=&#34;m&#34;&gt;301&lt;/span&gt; https://&lt;span class=&#34;nv&#34;&gt;$server_name$request_uri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;#Let&amp;#39;s encrypt endpoint&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        location &lt;span class=&#34;s1&#34;&gt;&amp;#39;/.well-known/acme-challenge&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          default_type &lt;span class=&#34;s2&#34;&gt;&amp;#34;text/plain&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          root        /tmp/letsencrypt-auto/blog/&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Repeat that setup for all your vhosts. Once you done that, you are finally ready to
create your let&amp;rsquo;s encrypt.&lt;/p&gt;
&lt;h1 id=&#34;lets-encrypt&#34;&gt;Let&amp;rsquo;s Encrypt&lt;/h1&gt;
&lt;p&gt;First, download the project&amp;rsquo;s client:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    git clone https://github.com/letsencrypt/letsencrypt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Now, we need to generate a certificate for &lt;strong&gt;each&lt;/strong&gt; of your sub-domains.
We also want to automatize this step in order to facilitate the certificate renewal. I created a zsh script
as follow:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/zsh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/tmp/letsencrypt-auto/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;yourwebsite.net&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;email&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;youremail@domain.net&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/root/letsencrypt/letsencrypt-auto --renew --debug&lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --email &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{email&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; --agree-tos certonly&lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --webroot -w &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; -d &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You need to specify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;acme&amp;rsquo;s webroot specified in your nginx configuration.&lt;/li&gt;
&lt;li&gt;the domain name you want to register.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do not forget to set the exec rights to that file for the root user.&lt;/p&gt;
&lt;p&gt;As you can see, I personally placed letsencrypt bin in my /root folder since you need to run it as root.
It can be a big deal for some people, for me it&amp;rsquo;s not.&lt;/p&gt;
&lt;p&gt;Now that we have our certificates, we want to automatize the renewal process. In order to do that, we need to
call our script using a cron entry.&lt;/p&gt;
&lt;p&gt;You just need to add this line to you root&amp;rsquo;s croontab:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; * * /root/letsencrypt/run.zsh &amp;gt;/dev/null 2&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This line will renew your certificate each 1st day of the month at midnight.&lt;/p&gt;
</description>
    </item>
    </channel>
</rss>
