Jekyll2019-11-01T15:08:40+00:00https://cah6.github.io/feed.xmlChristian HenryDetroit, MichiganTasting Night Recap: Champagne2019-09-02T00:00:00+00:002019-09-02T00:00:00+00:00https://cah6.github.io/drinks/champagne-1<p>I’ve been doing alcohol tasting nights at home for a while now, and want to get
into the habit of sharing them in case anybody else would benefit. Unsure if
I’ll backfill previous nights but for now I want to share the most recent:
Champagne! If you want to see the full presentation, go <a href="https://docs.google.com/presentation/d/1e0LAzFIUZJRmMUtaLSu_bzHCphJ9DrOsaHJLHHJrOsA/edit?usp=sharing">here</a>.</p>
<h1 id="quick-recap">Quick Recap</h1>
<p>More specifically, this one was all about things made in the traditional
champagne style, Méthode Champenoise. See the slides for more details but the
quick of it is that all 8 bottles went through the process of secondary
fermentation in the bottle, riddling (getting lees to bottom of bottle), and
disgorging (removing) the lees, even though only one was champagne proper.</p>
<p>The flight we had was:</p>
<ol>
<li>Antech Limoux: $13 - Limoux, France - from a French region that has been
making sparkling wine longer than the Champagne region</li>
<li>Loxarel Reserva 2012: $25 - Spain</li>
<li>Gruet Brut: $19 - New Mexico</li>
<li>Gruet Blanc de Noir: $19 - New Mexico</li>
<li>Moët & Chandon: $40 - Champagne, France</li>
<li>Heidrun Hawaiian Macadamia Nut: $22 - Point Reyes, CA - sparkling mead,
where the only difference between bottles is the honey used</li>
<li>Heidrun Arizona Desert Mesquite: $28 - Point Reyes, CA</li>
<li>Birra del Borgo L’Equilibrista: $?? (can’t remember) - Rome, Italy - 40%
wine, 60% beer, slightly sour</li>
</ol>
<p>General thoughts on the lineup, from talking to people:</p>
<ul>
<li>Antech Limoux held up very well for being significantly cheaper than all others</li>
<li>Moët was great but probably not worth the price if you’re not the most
discerning drinker</li>
<li>Heidrun was a crowd favorite, but different people preferred one vs the other
(which I consider quite a good thing)</li>
<li>If you like sour beers, you love Birra del Borgo</li>
</ul>
<p>And some notable/fun bits I learned while researching:</p>
<ul>
<li>riddling takes ~3 months by hand but can be done in less than a week by
machine</li>
<li>“Brut” is not the dryest style, “Brut Nature” is</li>
<li>Demi-Sec (one of the sweetest champagnes) was the most common ~150 years ago</li>
<li>Prosecco has 2 certification levels: DOC and DOCG, where DOCG is the higher
designation. Costco $7 prosecco is DOCG.</li>
<li>recommended to drink proper champagne around 47F-50F, which is likely warmer
than you would think</li>
</ul>
<p>Keeping this post short and simple, so that’s it. Cheers!</p>I’ve been doing alcohol tasting nights at home for a while now, and want to get into the habit of sharing them in case anybody else would benefit. Unsure if I’ll backfill previous nights but for now I want to share the most recent: Champagne! If you want to see the full presentation, go here.Exploring Nix & Haskell Part 3: Less Nix, More Reflex2019-04-21T00:00:00+00:002019-04-21T00:00:00+00:00https://cah6.github.io/technology/nix-haskell-3<p>In the last post, we ended off with a Nix-based environment with some basic workflow commands to build and test your Haskell code. I mentioned that my reason for investigating Nix environments in the first place was because it’s the most supported way to build a frontend Haskell application with <a href="https://reflex-frp.org/">Reflex</a> / <a href="https://github.com/ghcjs/ghcjs">GHCJS</a>.</p>
<p>In this post we’ll start with a project based off <a href="https://github.com/ElvishJerricco/reflex-project-skeleton">reflex-project-skeleton</a> and remove/add bits and pieces to it until we have a deployable (frontend only, for now) application with a prescribed IDE experience and workflow. It shouldn’t be strictly necessary to read the previous posts before this one, but it could help to put things in context.</p>
<p>To that end, this post will go over:</p>
<ul>
<li>other projects that have similar end goals</li>
<li>editing <a href="https://github.com/ElvishJerricco/reflex-project-skeleton">reflex-project-skeleton</a> to make it more streamlined for the frontend portion of a webapp</li>
<li>adding some workflow pieces, like hot reloading and IDE config</li>
<li>command line operations you’ll use often when developing on this type of project</li>
<li>common issues you’ll run into when adding more dependencies</li>
</ul>
<h2 id="a-quick-disclaimer-on-alternatives">A quick disclaimer on alternatives…</h2>
<p>A lot of the information in this post will parallel what <a href="https://github.com/obsidiansystems/obelisk">Obelisk</a> gives you.
For my own project, I ended up using the setup described here rather than Obelisk for the following reasons:</p>
<ul>
<li>Most of Obelisk’s commands are pass-throughs to nix-shell and nix-build operations. While it is a fairly thin wrapper on top of these commands, as a Nix noob it can be another layer of indirection that just adds complexity, especially when things go wrong. In its current state, you sometimes have to end up knowing what Obelisk is doing <em>as well as</em> the underlying Nix commands (which involves digging through a lot of source code). For this reason, I think reading through this post could help you even if you do decide that going through Obelisk is better for you.</li>
<li>I mostly develop on Mac and lots of the commands would not work for a variety of reasons. Usually this was because the underlying Nix operation wasn’t working, but for the above reason, this was slightly obscured from me.</li>
<li>Android and iOS builds would be theoretically nice but I didn’t care about it that much. I’d rather just get some website/web-app up, and just make it so that it has a nice mobile format.</li>
<li>I didn’t care about server-side rendering. I wanted my backend to be a fairly simple Haskell CRUD server and able to be on a more modern version of GHC if needed.</li>
<li>I didn’t want to pay for the frontend aspect of my project. My solution to this (which I’ll include in this post) is to use GitHub Pages, which makes makes some parts of Obelisk unnecessary.</li>
<li>Probably related to my lack of server-side rendering, the routing update broke my project. Since my backend was an independent REST service, I didn’t gain any benefit from whatever routing things it introduced, and there wasn’t much documentation on it so I didn’t really understand it either.</li>
</ul>
<p>That being said, a lot of these things have been fixed over the last few months, and you may want different things than me. The Obelisk folks have been very responsive to my questions, and I think the project has a good goal. Make sure to check it out to see if it’ll work for you; if it does, hopefully the information in this post will still help you there too!</p>
<h2 id="setting-up-the-basic-skeleton">Setting up the basic skeleton</h2>
<p>The goal for this section is to get the skeleton to a spot where we can comfortably learn the workflow commands and make future changes. We’ll start with the <a href="https://github.com/ElvishJerricco/reflex-project-skeleton">reflex-project-skeleton</a>, wiping history and re-committing:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone <span class="nt">--recurse-submodules</span> https://github.com/ElvishJerricco/reflex-project-skeleton my-haskell-reflex-project
<span class="nb">cd </span>my-haskell-reflex-project
<span class="nb">rm</span> <span class="nt">-rf</span> .git
git init
git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"Initial commit after cloning skeleton"</span>
</code></pre></div></div>
<p>You probably want to run <code class="language-plaintext highlighter-rouge">./reflex-platform/try-reflex</code> to set up and populate your caches. Some parts of this can take a while; if you’ve never tried reflex before, the <code class="language-plaintext highlighter-rouge">try-reflex</code> script will:</p>
<ol>
<li>Download / install Nix (fairly quick, it’s around 22MB)</li>
<li>Ask you to manually input whether to configure binary caches. Definitely say yes.</li>
<li>Download / build whatever is needed for Reflex / GHCJS environment. <em>This</em> part will take a while. To give you a general idea, I tried this on the default “hashicorp/precise64” Vagrant box with an internet connection of ~100 Mbit/s (fixed to match my usual speed with <a href="https://alexbilbie.com/2014/11/vagrant-dns/">this</a>) running on my 2016 Mac and it took about 30 minutes. I’d recommend letting this run in the background while you follow along with the rest of the setup. Keep in mind that this is roughly the lead time to expect if you ever update <code class="language-plaintext highlighter-rouge">reflex-platform</code> in the future.</li>
</ol>
<p>One thing you should notice is that the <code class="language-plaintext highlighter-rouge">default.nix</code> file is re-using the Nix expression from the reflex-platform submodule. We’re going to keep this, but know that this means that our entry point will be fundamentally different from the previous posts, where we built it up from scratch. Some settings are pre-filled in this <code class="language-plaintext highlighter-rouge">default.nix</code>; to see the full list (it’s not too long) check the reflex-platform/project/default.nix <a href="https://github.com/reflex-frp/reflex-platform/blob/7e002c573a3d7d3224eb2154ae55fc898e67d211/project/default.nix">here</a>.</p>
<h3 id="constraining-to-a-frontend-web-app">Constraining to a frontend web-app</h3>
<p>First, I want to simplify the scope here and not consider a backend and android/ios builds. Those can be a topic for another time. We’ll still keep the shells section though, so that this can be added in sometime later. So:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">rm</span> <span class="nt">-rf</span> backend
<span class="c"># edit default.nix to remove backend from packages and shells</span>
<span class="c"># edit default.nix to remove android/ios sections</span>
</code></pre></div></div>
<p>and you should end up with</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">reflex-platform</span> <span class="o">?</span> <span class="kr">import</span> <span class="sx">./reflex-platform</span> <span class="p">{}</span> <span class="p">}:</span>
<span class="nv">reflex-platform</span><span class="o">.</span><span class="nv">project</span> <span class="p">({</span> <span class="nv">pkgs</span><span class="p">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
<span class="nv">packages</span> <span class="o">=</span> <span class="p">{</span>
<span class="nv">common</span> <span class="o">=</span> <span class="sx">./common</span><span class="p">;</span>
<span class="nv">frontend</span> <span class="o">=</span> <span class="sx">./frontend</span><span class="p">;</span>
<span class="p">};</span>
<span class="nv">shells</span> <span class="o">=</span> <span class="p">{</span>
<span class="nv">ghc</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"common"</span> <span class="s2">"frontend"</span><span class="p">];</span>
<span class="nv">ghcjs</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"common"</span> <span class="s2">"frontend"</span><span class="p">];</span>
<span class="p">};</span>
<span class="p">})</span>
</code></pre></div></div>
<h3 id="using-nix-instead-of-git-submodules">Using Nix instead of git submodules</h3>
<p>The source skeleton uses a git submodule to “import” the reflex-platform nix file. I had some issues getting this to work in my cloned repo (probably because I wiped the history), but that’s fine – by now we should be pros at importing Nix things! We know (from part 1 of this series) that we can use <code class="language-plaintext highlighter-rouge">fetchFromGitHub</code> to get a repository, and <code class="language-plaintext highlighter-rouge">nix-prefetch-git</code> to find the correct sha256 value. Looking at the <a href="https://github.com/ElvishJerricco/reflex-project-skeleton">skeleton</a> again, we see that it’s pinning a <code class="language-plaintext highlighter-rouge">reflex-platform</code> commit that starts with <code class="language-plaintext highlighter-rouge">7e002c5</code>, so we can run:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-prefetch-git https://github.com/reflex-frp/reflex-platform.git 7e002c5
</code></pre></div></div>
<p>to get the full <code class="language-plaintext highlighter-rouge">rev</code> and <code class="language-plaintext highlighter-rouge">sha256</code> fields, then put it into a <code class="language-plaintext highlighter-rouge">nix/reflex-platform.nix</code> file that binds all this together:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">bootstrap</span> <span class="o">?</span> <span class="kr">import</span> <span class="o"><</span><span class="nv">nixpkgs</span><span class="o">></span> <span class="p">{}</span> <span class="p">}:</span>
<span class="kd">let</span>
<span class="nv">reflex-platform</span> <span class="o">=</span> <span class="nv">bootstrap</span><span class="o">.</span><span class="nv">fetchFromGitHub</span> <span class="p">{</span>
<span class="nv">owner</span> <span class="o">=</span> <span class="s2">"reflex-frp"</span><span class="p">;</span>
<span class="nv">repo</span> <span class="o">=</span> <span class="s2">"reflex-platform"</span><span class="p">;</span>
<span class="nv">rev</span> <span class="o">=</span> <span class="s2">"7e002c573a3d7d3224eb2154ae55fc898e67d211"</span><span class="p">;</span>
<span class="nv">sha256</span> <span class="o">=</span> <span class="s2">"1adhzvw32zahybwd6hn1fmqm0ky2x252mshscgq2g1qlks915436"</span><span class="p">;</span>
<span class="p">};</span>
<span class="kn">in</span>
<span class="kr">import</span> <span class="nv">reflex-platform</span> <span class="p">{}</span>
</code></pre></div></div>
<p>Use this in our <code class="language-plaintext highlighter-rouge">default.nix</code> by changing line 1 to import this file instead of looking at a sub-folder:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">reflex-platform</span> <span class="o">?</span> <span class="kr">import</span> <span class="sx">./nix/reflex-platform.nix</span> <span class="p">{}</span> <span class="p">}:</span>
<span class="o">...</span>
</code></pre></div></div>
<p>and remove the now unnecessary submodule pieces:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">rm</span> <span class="nt">-rf</span> reflex-platform
<span class="nb">rm</span> .gitmodules
</code></pre></div></div>
<h3 id="adding-hoogle-toggle">Adding Hoogle toggle</h3>
<p>Let’s also take as input whether hoogle is enabled, so that we don’t have to wait for Hoogle to build when we don’t need it:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">reflex-platform</span> <span class="o">?</span> <span class="kr">import</span> <span class="sx">./reflex-platform.nix</span> <span class="p">{}</span>
<span class="p">,</span> <span class="nv">withHoogle</span> <span class="o">?</span> <span class="kc">false</span>
<span class="p">}:</span>
<span class="nv">reflex-platform</span><span class="o">.</span><span class="nv">project</span> <span class="p">({</span> <span class="nv">pkgs</span><span class="p">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
<span class="kn">inherit</span> <span class="nv">withHoogle</span><span class="p">;</span>
<span class="o">...</span>
<span class="p">})</span>
</code></pre></div></div>
<h3 id="wiring-in-warp">Wiring in Warp</h3>
<p>Warp is what will provide us with a hot-reloading browser window <em>running GHC</em> when we save the source code. This will be very useful in our workflow, so let’s enable that by default. To do that, just set <code class="language-plaintext highlighter-rouge">useWarp = true;</code> in <code class="language-plaintext highlighter-rouge">default.nix</code>:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="nv">reflex-platform</span><span class="o">.</span><span class="nv">project</span> <span class="p">({</span> <span class="nv">pkgs</span><span class="p">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="p">{</span>
<span class="nv">withHoogle</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nv">useWarp</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="o">...</span>
</code></pre></div></div>
<p>which will start it on port 3003 by default. You can change this port by setting the <code class="language-plaintext highlighter-rouge">JSADDLE_WARP_PORT</code> environment variable. You’ll know it’s working since it will print out</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Running jsaddle-warp server on port 3003
</code></pre></div></div>
<p>when <code class="language-plaintext highlighter-rouge">main</code> is ran.</p>
<p>Note: you previously had to manually import <code class="language-plaintext highlighter-rouge">jsaddle-warp</code>, import <code class="language-plaintext highlighter-rouge">Reflex.Dom.Core</code> instead of <code class="language-plaintext highlighter-rouge">Reflex.Dom</code>, and wrap <code class="language-plaintext highlighter-rouge">widgetMain</code> with <code class="language-plaintext highlighter-rouge">run 3003 $</code> but this shouldn’t be necessary anymore.</p>
<h3 id="miscellaneous-cleanup-and-additions">Miscellaneous cleanup and additions</h3>
<p>Reflex-platform will give you cabal and other dev tools by default, so we don’t need to touch <code class="language-plaintext highlighter-rouge">shellToolOverrides</code> in <code class="language-plaintext highlighter-rouge">default.nix</code>, which pretty much does what the <code class="language-plaintext highlighter-rouge">buildInputs</code> param did in the previous post. However, let’s just add in empty sections for that and <code class="language-plaintext highlighter-rouge">overrides</code> in case we do need it sometime:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="nv">shellToolOverrides</span> <span class="o">=</span> <span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="p">};</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="p">};</span>
<span class="o">...</span>
</code></pre></div></div>
<p>As you’ll see in the next section, I don’t really use the <code class="language-plaintext highlighter-rouge">cabal</code> helper scripts, so I’m going to go ahead and remove them (if you find them helpful, feel free to keep them):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">rm </span>cabal
<span class="nb">rm </span>cabal-ghcjs
<span class="nb">rm </span>cabal-ghcjs.project <span class="c"># without a backend, we only have one "project"</span>
</code></pre></div></div>
<p>and ignore intermediate things in the <code class="language-plaintext highlighter-rouge">.gitignore</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dist*
frontend-result
**/*.hi
**/*.o
.ghc*
</code></pre></div></div>
<h3 id="setting-up-visual-studio-code">Setting up Visual Studio Code</h3>
<p>At this point, this guide is pretty committed to using Visual Studio Code so we’ll make a settings override that will work with our project structure. The default settings set you up pretty well, but we’ll also use <code class="language-plaintext highlighter-rouge">:set -i</code> (a ghci command) to include the <code class="language-plaintext highlighter-rouge">frontend/src</code> and <code class="language-plaintext highlighter-rouge">common/src</code> folders. The plugin is usually pretty good about figuring out your project type but I have had it incorrectly default to Stack, so we’ll specify that directly. Note that, for now, this is non-new-style cabal. All together your <code class="language-plaintext highlighter-rouge">.vscode/settings.json</code> file should look like:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"ghcSimple.startupCommands.custom"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">":set -fno-diagnostics-show-caret -fdiagnostics-color=never -ferror-spans"</span><span class="p">,</span><span class="w">
</span><span class="s2">":set -fdefer-type-errors -fdefer-typed-holes -fdefer-out-of-scope-variables"</span><span class="p">,</span><span class="w">
</span><span class="s2">":set -ifrontend/src:common/src"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"ghcSimple.workspaceType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cabal"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p><em>Now</em> we have a skeleton that will work with everything in the next section. To see the code tagged now, see <a href="https://github.com/cah6/haskell-nix-skeleton-2/tree/barebones_webapp">here</a>.</p>
<h2 id="workflow-commands">Workflow commands</h2>
<p>I mentioned some of these in the past, but let’s get a complete list of commands you need to know while you’re working in this environment. Most of these need to be ran in the GHC nix-shell, which you launch with:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-shell <span class="nt">-A</span> shells.ghc
</code></pre></div></div>
<p>and then run the command in it. If you want to run it in one single-fire command, enter it in the form:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-shell <span class="nt">-A</span> shells.ghc <span class="nt">--run</span> <span class="s2">"<command>"</span>
</code></pre></div></div>
<h3 id="start-up-ide">Start up IDE</h3>
<p>As an example of above: since we want to primarily develop on GHC (it’s faster), we start up the GHC shell:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-shell <span class="nt">-A</span> shells.ghc
</code></pre></div></div>
<p>and launch Visual Studio Code from there:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code <span class="nb">.</span>
</code></pre></div></div>
<p>Or as one command:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-shell <span class="nt">-A</span> shells.ghc <span class="nt">--run</span> <span class="s2">"code ."</span>
</code></pre></div></div>
<h3 id="fire-up-ghcid">Fire up ghcid</h3>
<p>Note: this is basically what Obelisk’s <code class="language-plaintext highlighter-rouge">ob run</code> does.</p>
<p>The main benefit of ghcid here is that we can hot-reload our app a few seconds after saving our source file. Another benefit is that you can see the full compile error message (sometimes it gets formatted weird on the IDE), and you can have a “sanity test” for your IDE if it freezes or something. We start ghcid like this by running (in the GHC shell):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ghcid <span class="nt">-W</span> <span class="nt">-c</span> <span class="s2">"cabal new-repl frontend"</span> <span class="nt">-T</span> Main.main
</code></pre></div></div>
<p>To break that down:</p>
<ul>
<li>-W lets you run tests even if there’s warnings</li>
<li>-T runs that method whenever a re-compile is done</li>
<li>-c specifies the shell/repl to run that command in. We want new-repl since we have a <code class="language-plaintext highlighter-rouge">cabal.project</code> file in our repo.</li>
</ul>
<p>Since this is something you want to fire once and keep running, you probably want the single command to copy-paste:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-shell <span class="nt">-A</span> shells.ghc <span class="nt">--run</span> <span class="s1">'ghcid -W -c "cabal new-repl frontend" -T Main.main'</span>
</code></pre></div></div>
<p>And then you can go to <code class="language-plaintext highlighter-rouge">localhost:3003</code> (or whatever port you have set in your entry point) to see your application.</p>
<h3 id="fire-up-a-repl">Fire up a repl</h3>
<p>Note: this is basically what Obelisk’s <code class="language-plaintext highlighter-rouge">ob repl</code> does.</p>
<p>You shouldn’t be using this for compile errors, but sometimes you want a repl to test out small sections of code. Do this by firing up the same repl ghcid does, i.e:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-shell <span class="nt">-A</span> shells.ghc <span class="nt">--run</span> <span class="s2">"cabal new-repl frontend"</span>
</code></pre></div></div>
<h3 id="start-hoogle">Start Hoogle</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-shell <span class="nt">-A</span> shells.ghc <span class="nt">--run</span> <span class="nt">--arg</span> withHoogle <span class="nb">true</span> <span class="s2">"hoogle server --local --port=8080"</span>
</code></pre></div></div>
<p>Note that we could have also done what we did in previous posts where we define a nix-shell in a new file that passes in <code class="language-plaintext highlighter-rouge">withHoogle = true</code> and run the hoogle command in there. Either works.</p>
<h3 id="build-frontend-application">Build frontend application</h3>
<p>You probably don’t want to do this very often, since ghcjs will compile much slower than ghc. However, I’d recommend doing this whenever you edit your cabal files, since your ghcjs dependencies could require some updating too, and it can be frustrating to do this in one push when you want to deploy. So to build the final thing, we’ll use nix-build with:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-build <span class="nt">-o</span> frontend-result <span class="nt">-A</span> ghcjs.frontend
</code></pre></div></div>
<h3 id="deploy-your-application">“Deploy” your application</h3>
<p>In your github project settings, make sure you have GitHub Pages enabled with the option to build from the <code class="language-plaintext highlighter-rouge">/docs</code> folder. With this, all you need to do is copy the stuff in <code class="language-plaintext highlighter-rouge">frontend-result</code> to docs:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">rm</span> <span class="nt">-rf</span> docs<span class="p">;</span> <span class="nb">cp</span> <span class="nt">-r</span> frontend-result/bin/frontend.jsexe docs
</code></pre></div></div>
<p>and push the updated docs folder to github.</p>
<p>Before pushing, you may want to test out the final ghcjs app by opening it in your browser:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open docs/index.html
</code></pre></div></div>
<p>Once you push the <code class="language-plaintext highlighter-rouge">docs/</code> folder, your Haskell GHCJS application will be available at <code class="language-plaintext highlighter-rouge">https://name.github.io/repo-name</code>. In the case of this repository, that would be https://cah6.github.io/haskell-nix-skeleton-2/.</p>
<p>I know this method isn’t for everybody, but if you have strong counter-opinions to doing this you probably have enough knowledge or desire to host the docs folder elsewhere.</p>
<h3 id="all-together-now">All together now</h3>
<p>All this means that I usually have a number of terminals (in my case iTerm split windows) open:</p>
<ol>
<li>One terminal for a consistent Hoogle db running in foreground</li>
<li>One terminal for a consistent ghcid running in foreground</li>
<li>One terminal sitting in <code class="language-plaintext highlighter-rouge">nix-shell -A shells.ghc</code> to re-launch the IDE or pop into a repl.</li>
<li>One terminal sitting in <code class="language-plaintext highlighter-rouge">nix-shell -A shells.ghcjs</code> to rebuild the final frontend.</li>
</ol>
<p>Organizing them in the foreground like this means that it’s easier to restart all 4 when you change a package in your cabal file / Nix expression.</p>
<h2 id="adding-some-dependencies">Adding some dependencies</h2>
<p>Now that we have these core commands out of the way, let’s make some changes to the project to illustrate common types of environment changes you’ll need to make. This is pretty similar to the info in <a href="https://github.com/Gabriel439/haskell-nix">https://github.com/Gabriel439/haskell-nix</a>, but this can be really confusing for newcomers, so I think it’s worth re-showing.</p>
<h3 id="dontcheck-to-skip-tests">dontCheck to skip tests</h3>
<p>Let’s say our frontend needed the package <a href="http://hackage.haskell.org/package/http-media">http-media</a>. First, add that package to the <code class="language-plaintext highlighter-rouge">frontend.cabal</code> file. Re-entering our <code class="language-plaintext highlighter-rouge">nix-shell -A shells.ghc</code> works quickly but re-entering <code class="language-plaintext highlighter-rouge">nix-shell -A shells.ghcjs</code> makes it build and test the package from scratch, since it’s not in the cached set of packages for reflex-platform.</p>
<p>If we don’t care about running the tests for this package, whether because it takes too long, can’t compile, or can’t pass with the package combination we have, we can override it with the <code class="language-plaintext highlighter-rouge">dontCheck</code> function in our <code class="language-plaintext highlighter-rouge">default.nix</code>:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">http-media</span> <span class="o">=</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">haskell</span><span class="o">.</span><span class="nv">lib</span><span class="o">.</span><span class="nv">dontCheck</span> <span class="nv">super</span><span class="o">.</span><span class="nv">http-media</span><span class="p">;</span>
<span class="p">};</span>
<span class="o">...</span>
</code></pre></div></div>
<p>Another reason to use <code class="language-plaintext highlighter-rouge">dontCheck</code> is if the test suites depend on incompatible packages. The biggest culprit here is the <code class="language-plaintext highlighter-rouge">doctest</code> package, which <a href="https://github.com/NixOS/nixpkgs/issues/47437#issuecomment-429657590">can’t build on GHCJS</a>. For example, the <a href="https://github.com/lens/lens-aeson/blob/master/lens-aeson.cabal">lens-aeson</a> library has a doctests test-suite, so you’ll need to run it through <code class="language-plaintext highlighter-rouge">dontCheck</code> in order to use it.</p>
<h3 id="pinning-a-library-through-cabal2nix">Pinning a library through cabal2nix</h3>
<p>Let’s say we need the <code class="language-plaintext highlighter-rouge">servant-reflex</code> package now. Adding this to our frontend cabal file and re-entering the GHC shell will fail with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Setup: Encountered missing dependencies:
base >=4.8 && <4.10,
exceptions ==0.8.*,
http-media ==0.6.*,
servant >=0.8 && <0.13
</code></pre></div></div>
<p>because this package uses strict bounds on dependencies and the version it decided to use (v0.3.3) doesn’t mesh with what we have. The latest commit (v0.3.4) does though, so we can generate a Nix file pointing to the latest commit with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cabal2nix https://github.com/imalsogreg/servant-reflex > nix/servant-reflex.nix
</code></pre></div></div>
<p>or for a specific commit:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cabal2nix https://github.com/imalsogreg/servant-reflex --revision ba8d4f8 > nix/servant-reflex.nix
</code></pre></div></div>
<p>Then call it in our <code class="language-plaintext highlighter-rouge">default.nix</code> overrides:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">servant-reflex</span> <span class="o">=</span> <span class="nv">self</span><span class="o">.</span><span class="nv">callPackage</span> <span class="sx">./nix/servant-reflex.nix</span> <span class="p">{</span> <span class="p">};</span>
<span class="o">...</span>
<span class="p">};</span>
<span class="o">...</span>
</code></pre></div></div>
<p>Make sure to use <code class="language-plaintext highlighter-rouge">callPackage</code> from <code class="language-plaintext highlighter-rouge">self</code> (<a href="https://nixos.org/nixpkgs/manual/#sec-overlays-definition">the final package set</a>) instead of <code class="language-plaintext highlighter-rouge">pkgs.haskellPackages</code> to make sure the package you’re calling is getting any other overridden packages.</p>
<h3 id="pinning-through-the-libarys-defaultnix-file">Pinning through the libary’s default.nix file</h3>
<p>The <code class="language-plaintext highlighter-rouge">servant-reflex</code> library has a Nix file defined in its repository, so you might be wondering why we generated our own instead of just using theirs. I tried this first, but encountered a strange issue where including it as a dependency via their Nix file somehow turned off <code class="language-plaintext highlighter-rouge">jsaddle-warp</code> when my app was launched with GHC. I’m not sure why this happened, but did notice that using their Nix expression pulled in more dependencies since it also tried to satisfy and build the <code class="language-plaintext highlighter-rouge">executable example</code> section. On the other hand, <code class="language-plaintext highlighter-rouge">cabal2nix</code> knows that we only want the package as a library, and so will only build the library section of the cabal file.</p>
<p>My suggestion would be to be careful using the repository’s Nix derivation for packages that provide more than just a <code class="language-plaintext highlighter-rouge">libary</code> build in the cabal file, and maybe only try the below method if using <code class="language-plaintext highlighter-rouge">cabal2nix</code> doesn’t work with that library.</p>
<p>If you do need to use their custom Nix file, do that by getting the full rev/sha256 with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-prefetch-git https://github.com/imalsogreg/servant-reflex
</code></pre></div></div>
<p>putting that info into the <code class="language-plaintext highlighter-rouge">nix/servant-reflex.nix</code> file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{ bootstrap ? import <nixpkgs> {} }:
bootstrap.fetchFromGitHub {
owner = "imalsogreg";
repo = "servant-reflex";
rev = "44595630e2d1597911ecb204e792d17db7e4a4ee";
sha256 = "009d8vr6mxfm9czywhb8haq8pwvnl9ha2cdmaagk1hp6q4yhfq1n";
}
</code></pre></div></div>
<p>and then putting the same <code class="language-plaintext highlighter-rouge">callPackage</code> as the above section in your <code class="language-plaintext highlighter-rouge">default.nix</code> overrides.</p>
<h2 id="ending-notes">Ending Notes</h2>
<p>If you want to play around with the barebones skeleton in your own repository, just do something similar to the initial skeleton clone:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/cah6/haskell-nix-skeleton-2/tree/barebones_webapp my-haskell-widgets
<span class="nb">cd </span>my-haskell-reflex-project
<span class="nb">rm</span> <span class="nt">-rf</span> .git
git init
git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"Initial commit after cloning skeleton"</span>
</code></pre></div></div>
<p>Hopefully this post has helped you get started in making widgets in Haskell and providing an easy way to show them off to your friends. Friend send friends widgets made in Haskell, right?</p>
<p>If you want to learn more Reflex now that you have a good test environment, I would recommend <a href="https://blog.qfpl.io/posts/reflex/basics/introduction/">the qfpl tutorial</a> for concepts, <a href="https://github.com/hansroland/reflex-dom-inbits/blob/master/tutorial.md">reflex-dom-inbits</a> for helpful bits of knowledge, and <a href="https://github.com/reflex-frp/reflex/blob/develop/Quickref.md">the quickref</a> for a function cheat sheet.</p>
<p>As always, if you’d like to comment on this post please do so in the associated reddit post <a href="https://www.reddit.com/r/haskell/comments/bfriv7/exploring_nix_haskell_part_3_less_nix_more_reflex/">here</a>.</p>In the last post, we ended off with a Nix-based environment with some basic workflow commands to build and test your Haskell code. I mentioned that my reason for investigating Nix environments in the first place was because it’s the most supported way to build a frontend Haskell application with Reflex / GHCJS.Exploring Nix & Haskell Part 2: Dev Tools & IDE Integration2019-04-18T00:00:00+00:002019-04-18T00:00:00+00:00https://cah6.github.io/technology/nix-haskell-2<p>In the <a href="https://cah6.github.io/technology/nix-haskell-1/">previous post from a loong time ago</a>,
I left off with a simple <code class="language-plaintext highlighter-rouge">shell.nix</code> file that you could use to run a local hoogle server.</p>
<p>In this post, I’ll build on that to show how you can add dependencies to your
dev environment, then use that to bring up a no-fuss Haskell environment with a solid IDE
experience. Be warned that the IDE section is fairly opinionated – if you have one
that works for you, great! However, I think a lot of people don’t know where to start,
and hopefully this will help in establishing a starting point.</p>
<h2 id="adding-dev-tools">Adding Dev Tools</h2>
<p>Last time, we entered a nix shell with the env provided by our <code class="language-plaintext highlighter-rouge">releaseX.nix</code> by default.
This contained ghc/ghci, but not much else. To add more, we’ll need to “override
attributes” in this env. Notably, you’ll probably want to edit <code class="language-plaintext highlighter-rouge">buildInputs</code>
(which will load programs into our <code class="language-plaintext highlighter-rouge">nix-shell</code>) and <code class="language-plaintext highlighter-rouge">shellHook</code> (which will run
whatever code block we provide when the shell is entered). You should <em>definitely</em>
install cabal this way (even if you have a globally installed cabal), so that your GHC and
cabal versions play nicely together. Note that your <code class="language-plaintext highlighter-rouge">releaseX.nix</code> file only exposes
your project, so you’ll need to re-import your pinned nixpkgs in this shell file to get the other
haskell packages you want. In practice, putting this all together with a few more
dev tools looks <code class="language-plaintext highlighter-rouge">shell2.nix</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let
pinnedPkgs = import ./pkgs-from-json.nix { json = ./nixos-18-09.json; };
myPackages = (import ./release6.nix { withHoogle = true; } );
projectDrvEnv = myPackages.project1.env.overrideAttrs (oldAttrs: rec {
buildInputs = oldAttrs.buildInputs ++ [
# required!
pinnedPkgs.haskellPackages.cabal-install
# optional
pinnedPkgs.haskellPackages.hlint
pinnedPkgs.haskellPackages.hsimport
];
shellHook = ''
export USERNAME="christian.henry"
'';
});
in
projectDrvEnv
</code></pre></div></div>
<p>The best thing about this is that your shell inputs don’t have to be haskell packages!
If you have a project that needs elasticsearch, you could add <code class="language-plaintext highlighter-rouge">pinnedPkgs.elasticsearch5</code>
to the <code class="language-plaintext highlighter-rouge">buildInputs</code>, add startup commands as needed, and fire it up! The nice
thing about doing tools this way is:</p>
<ol>
<li>It reduces the number of manual steps for anybody that wants to develop on your
project.</li>
<li>You ensure everybody is using the same version of these dependencies.</li>
<li>There’s no conflict between other projects with similar dependencies, but needing
different versions – each project has its own sandbox.</li>
</ol>
<p>As another example: I recently wanted to try out elm as a frontend for a haskell backend. I could
have installed it globally, but I needed elm 0.18 which is not very easy to find anymore.
I did, however, find that elm 0.18 is the default version on the Nix 18.03 channel, so all I
had to do to get the correct version in my shell was import that channel and
load up the right package:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let
elmPkgs = import ./pkgs-from-json.nix { json = ./nixos-18-03.json; };
pinnedPkgs = import ./pkgs-from-json.nix { json = ./nixos-18-09.json; };
myPackages = (import ./release6.nix { withHoogle = true; } );
projectDrvEnv = myPackages.project1.env.overrideAttrs (oldAttrs: rec {
buildInputs = oldAttrs.buildInputs ++ [
...
elmPkgs.elmPackages.elm
...
shellHook = ''
...
'';
});
in
projectDrvEnv
</code></pre></div></div>
<p>Note that in this case it’s fine to mix and match Nix channels because the packages
don’t rely on each other at all.</p>
<h2 id="quick-ide-survey">Quick IDE Survey</h2>
<p>Now that we have whatever dev tools we need, let’s focus on setting up the actual
IDE experience. If you want to have a Haskell IDE with a Nix project, there are a couple options.
I’ve tried:</p>
<ul>
<li>Dante for emacs</li>
<li>Haskell IDE Engine (HIE) with [Atom, Sublime, Visual Studio Code, Emacs, etc]</li>
<li>ghc-simple for Visual Studio Code</li>
</ul>
<p>I also want to quickly mention intellij-haskell. Coming from Java, I’m used to
intellij and find that this plugin provides an excellent overall dev experience. However,
it’s tied to Stack projects and isn’t suitable, as far as I can tell, for projects built with Nix. If your
project is using Stack, check it out too! For what it’s worth, my main reason
for using Nix over Stack was to be able to work on projects using <a href="https://reflex-frp.org/">Reflex</a>,
which is more supported through the Nix ecosystem.</p>
<p>Here’s my experience with each:</p>
<p>Dante is pretty good, but ultimately I couldn’t be bothered to learn emacs along
with everything else. If you’re already comfortable there, try it out!</p>
<p>HIE consistently looks promising but can be a frustrating end user experience. While it has wide adoption
and looks to be the converging point for a lot of other tools, I’ve had lots of issues getting
it fully working. I would get through cache issues, installing the version
corresponding to my GHC version, linking that to my project, whatever other errors
sprang up, and it might work in some way for some time but would eventually hit something else. Note that this evaluation was around 6 months ago; it could be better now but it doesn’t really matter to me because…</p>
<p>I decided to try out <a href="https://github.com/dramforever/vscode-ghc-simple">vscode-ghc-simple</a>
and love it. It “just works” in a way that lets you focus on actually writing
haskell code, and works with any project that has GHC 8.0+. There is almost zero
project setup and it works with most environment setups.</p>
<h2 id="setting-up-ghc-simple">Setting up ghc-simple</h2>
<p>To set this up, all you need to do is:</p>
<ol>
<li><a href="https://code.visualstudio.com/">Install vscode</a>.</li>
<li>Install the <code class="language-plaintext highlighter-rouge">code</code> shell command by opening the command palette (cmd+shift+p), type
“shell command”, select.</li>
<li>Install the “Simple GHC (Haskell) Integration” extension. It should automatically
install “Haskell Syntax Highlighting” which detects Haskell files and styles them.</li>
</ol>
<p>Then from your project root directory, run</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ nix-shell shell.nix
$ code .
</code></pre></div></div>
<p>to launch vscode with your project’s GHC loaded up. Go to some Haskell files, edit
them, you should see compile errors. That’s about it!</p>
<p>If for some reason you don’t see compile errors or the plugin doesn’t seem to be
working, it can be helpful to check the plugin logs. The default location for these
are:</p>
<ul>
<li>NixOS: ~/.config/Code/logs</li>
<li>Mac: ~/Library/Application\ Support/Code/</li>
</ul>
<p>and you can find the most recent logs by digging into this general area:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>logs/{last folder (ordered by date)}/extHost#/output_logging_XXX/#-GHC.log
</code></pre></div></div>
<h2 id="my-workflow">My workflow</h2>
<p>That being said, since your packages are tied to your shell, you need to do a little
reloading if you add a haskell dependency to your project.</p>
<p>But first, let’s make a little quality of life change to our Nix release file. I
routinely forget to run “cabal2nix” when updating my cabal file, so let’s eliminate
that manual step by using <code class="language-plaintext highlighter-rouge">callCabal2nix</code> instead of <code class="language-plaintext highlighter-rouge">callPackage</code>. The only
change necessary for this is to replace</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>project1 = self.callPackage ./default.nix { };
</code></pre></div></div>
<p>with</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>project1 = self.callCabal2nix "project1" ./project1.cabal { };
</code></pre></div></div>
<p>With this change, my process for adding a new dependency is:</p>
<ol>
<li>List it in your cabal file.</li>
<li><code class="language-plaintext highlighter-rouge">exit</code> your <code class="language-plaintext highlighter-rouge">nix-shell</code>.</li>
<li>Reload it with <code class="language-plaintext highlighter-rouge">nix-shell shell.nix</code>. This will need to re-build your Hoogle database,
which takes around 2-3 minutes. Note that <a href="https://github.com/target/lorri">lorri</a> looks to be a good tool for automatically doing this in the background for you, though I haven’t tested it out yet.</li>
<li>Restart Hoogle with your new shell.</li>
<li>Exit vscode and relaunch with <code class="language-plaintext highlighter-rouge">code .</code> in your new shell. It should take ~5-10 seconds
for errors and such to show up in the editor as it connects to ghci.</li>
</ol>
<p>At this point, you should have all the tools for a good workflow:</p>
<ul>
<li>editor for quick feedback on compile errors</li>
<li>pinned Hoogle tab for searching through library documentation</li>
<li><code class="language-plaintext highlighter-rouge">cabal new-repl</code> for one-off tests of low-scope functions</li>
<li><code class="language-plaintext highlighter-rouge">cabal run</code> for running the entire program</li>
</ul>
<p>Some assorted things I’ve found while using this workflow:</p>
<ul>
<li>If you put <code class="language-plaintext highlighter-rouge">default-extensions</code> in your cabal file instead of at the top of
every haskell file (lots easier), you should load your REPL with <code class="language-plaintext highlighter-rouge">cabal new-repl</code>
instead of <code class="language-plaintext highlighter-rouge">ghci</code>, as that will go through your cabal file.</li>
<li>Sometimes entering nix shell will fail with something like <code class="language-plaintext highlighter-rouge">package … cannot be satisfied</code>.
If that happens, it can usually be fixed by manually deleting your <code class="language-plaintext highlighter-rouge">.ghc.environment*</code>
directory that cabal new-style commands produce.</li>
</ul>
<h2 id="ending-notes">Ending Notes</h2>
<p>That’s it for this post – my next one will show how to set up a
Reflex project using Nix. That will hopefully come out faster than this one did (sorry)! It hasn’t
changed much, but the code associated with this can be found <a href="https://github.com/cah6/haskell-nix-skeleton-1">here</a>.</p>
<p>If you’d like to comment on any part of this post, please do so in the associated
reddit post <a href="https://www.reddit.com/r/haskell/comments/beq792/exploring_nix_haskell_part_2_dev_tools_ide/">here</a>! Feel free to also message me directly.</p>In the previous post from a loong time ago, I left off with a simple shell.nix file that you could use to run a local hoogle server.Exploring Nix & Haskell Part 1: Project Setup2018-08-08T00:00:00+00:002018-08-08T00:00:00+00:00https://cah6.github.io/technology/nix-haskell-1<p>I’ve been working a lot with Nix and Haskell lately, and I thought now would be
a good time to pause and write about it before I either forget what I’ve learned
or forget why I found it difficult in the first place.</p>
<p>This is the first of a series of posts, with the overarching goal of building up a
deterministic Haskell development environment using Nix, including a modern IDE experience and any tooling
built-in. I’ve seen a lot of Haskell posts recently expressing frustration
at the initial developer experience, and hopefully this can help alleviate that.
This means that while we’ll end each post with a valid, working environment, along
the way I’ll try to also explain the individual pieces as much as I understand them, so we can build up
more of an intuition of what each piece does.</p>
<p>This first post will cover:</p>
<ul>
<li>why choose Nix for your Haskell environment</li>
<li>pinning haskellPackages for your project</li>
<li>setting the GHC version you want</li>
<li>overriding haskell packages</li>
<li>getting documentation for your packages and enabling Hoogle</li>
</ul>
<p>I will assume basic knowledge of Nix (i.e. <a href="https://learnxinyminutes.com/docs/nix/">this</a> makes sense to you)
and some basic knowledge of the Haskell ecosystem (but not much). Having read project0 and project1 of <a href="https://github.com/Gabriel439/haskell-nix">https://github.com/Gabriel439/haskell-nix</a>
is also probably useful. I will try to provide full in-line examples wherever possible
so you can follow along; if you get stuck, the end code for this post, with intermediate steps, can
be found <a href="https://github.com/cah6/haskell-nix-skeleton-1">here</a>.</p>
<h2 id="why-nix">Why Nix</h2>
<p>First of all, why use Nix for your development environment at all instead of Stack, the main alternative?
To be clear, I think Stack is quite good at getting a development environment up
quickly and painlessly – if you’d like to read more about it, the <a href="https://docs.haskellstack.org/en/stable/GUIDE/">stack guide</a>
is pretty good, and the first few sections of <a href="https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/">this post</a>
are awesome for understanding how to get it working with your IDE and explaining some of
the gotchas.</p>
<p>With that aside, there are a few reasons for choosing Nix-based development:</p>
<ol>
<li>Nix makes managing non-Haskell dependencies pretty easy</li>
<li>With Nix, you can make your entire developer environment completely reproducible,
in the style of a lightweight <a href="https://www.vagrantup.com/intro/index.html">vagrant</a>. Think:
all command line tools declaratively defined, IDE with all the right plugins, etc.</li>
<li>Most GHCJS frameworks don’t play too well with Stack. I’ve been playing around
with <a href="https://github.com/reflex-frp/reflex-platform">reflex</a> and <a href="https://github.com/obsidiansystems/obelisk">obelisk</a>
recently, and while you could probably get things working eventually
with Stack, it’s not the primary, supported method.</li>
<li>Nix is more powerful. It’s a full language and, while I think it’s more complicated,
it does have a <em>MUCH</em> higher upper bound for what you can do with it.</li>
</ol>
<h2 id="setup">Setup</h2>
<p>Enough why, let’s get started! We’re going to use a cabal file that’s pretty basic but has a few extra dependencies
so we can see when things are getting downloaded / built from source:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>name: nix-test
version: 0.1.0.0
license: BSD3
license-file: LICENSE
build-type: Simple
extra-source-files: ChangeLog.md
cabal-version: >=1.10
executable nix-test
main-is: Main.hs
build-depends: base >=4.9
, containers
, lens
, text
hs-source-dirs: src
default-language: Haskell2010
</code></pre></div></div>
<p>Then you’ll want to do a <code class="language-plaintext highlighter-rouge">cabal2nix . > default.nix</code> to generate a derivation from that.
Remember to run cabal2nix any time you change your cabal file! Note that by default, Nix will require a LICENSE file for your project.</p>
<p>Also, make sure your Nix version is at least 2.0. You can check with <code class="language-plaintext highlighter-rouge">nix --version</code>, and
according to their manual you can update to latest version with:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-channel --update; nix-env -iA nixpkgs.nix
</code></pre></div></div>
<p>This is important because some command line tools have changed between versions.
As an example we’ll be using, the command to enter the repl changed from <code class="language-plaintext highlighter-rouge">nix-repl</code> to <code class="language-plaintext highlighter-rouge">nix repl</code>,
and some commands won’t work if you launch it with <code class="language-plaintext highlighter-rouge">nix-repl</code>, since it explicitly depends on
and launches using Nix 1.</p>
<h2 id="pinning-your-haskell-packages">Pinning your haskell packages</h2>
<p>The absolute minimum derivation, which we’ll be building on (call it <code class="language-plaintext highlighter-rouge">release.nix</code>), is as follows:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span>
<span class="nv">pkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="o"><</span><span class="nv">nixpkgs</span><span class="o">></span> <span class="p">{</span> <span class="p">};</span>
<span class="kn">in</span>
<span class="p">{</span> <span class="nv">project1</span> <span class="o">=</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">haskellPackages</span><span class="o">.</span><span class="nv">callPackage</span> <span class="sx">./default.nix</span> <span class="p">{</span> <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The main thing I don’t like about this is that it uses whatever “nixpkgs” is floating around
your system. Instead, let’s pin to a specific commit of a stable channel using
nix-prefetch-git:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nix-env <span class="nt">-i</span> nix-prefetch-git <span class="c"># if you don't have it</span>
<span class="nv">$ </span>nix-prefetch-git https://github.com/nixos/nixpkgs-channels.git refs/heads/nixos-18.03 <span class="o">></span> nixos-18-03.json
</code></pre></div></div>
<p>which generates:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://github.com/nixos/nixpkgs-channels.git"</span><span class="p">,</span><span class="w">
</span><span class="nl">"rev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8b92a4e600458c01ab0a72f2492eb7120e18f9bc"</span><span class="p">,</span><span class="w">
</span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2018-09-02T16:06:16+02:00"</span><span class="p">,</span><span class="w">
</span><span class="nl">"sha256"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1s28c24wydfiqkbz9x7bwhjnh2x4qr010p18si7xdnfdwrxn5mh1"</span><span class="p">,</span><span class="w">
</span><span class="nl">"fetchSubmodules"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Then to use it in your nix file, you’d use a combination of readFile, fromJSON,
and fetchFromGitHub. In it’s own file (call it <code class="language-plaintext highlighter-rouge">pkgs-from-json.nix</code>), this looks like:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="nv">bootstrap</span> <span class="o">?</span> <span class="kr">import</span> <span class="o"><</span><span class="nv">nixpkgs</span><span class="o">></span> <span class="p">{}</span>
<span class="p">,</span> <span class="nv">json</span>
<span class="p">}:</span>
<span class="kd">let</span>
<span class="nv">nixpkgs</span> <span class="o">=</span> <span class="kr">builtins</span><span class="o">.</span><span class="nv">fromJSON</span> <span class="p">(</span><span class="kr">builtins</span><span class="o">.</span><span class="nv">readFile</span> <span class="nv">json</span><span class="p">);</span>
<span class="nv">src</span> <span class="o">=</span> <span class="nv">bootstrap</span><span class="o">.</span><span class="nv">fetchFromGitHub</span> <span class="p">{</span>
<span class="nv">owner</span> <span class="o">=</span> <span class="s2">"NixOS"</span><span class="p">;</span>
<span class="nv">repo</span> <span class="o">=</span> <span class="s2">"nixpkgs-channels"</span><span class="p">;</span>
<span class="kn">inherit</span> <span class="p">(</span><span class="nv">nixpkgs</span><span class="p">)</span> <span class="nv">rev</span> <span class="nv">sha256</span><span class="p">;</span>
<span class="p">};</span>
<span class="kn">in</span>
<span class="kr">import</span> <span class="nv">src</span> <span class="p">{}</span>
</code></pre></div></div>
<p>and is used in <code class="language-plaintext highlighter-rouge">release.nix</code> by changing that file to:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span>
<span class="nv">pinnedPkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="sx">./pkgs-from-json.nix</span> <span class="p">{</span> <span class="nv">json</span> <span class="o">=</span> <span class="sx">./nixos-18-03.json</span><span class="p">;</span> <span class="p">};</span>
<span class="kn">in</span>
<span class="p">{</span> <span class="nv">project1</span> <span class="o">=</span> <span class="nv">pinnedPkgs</span><span class="o">.</span><span class="nv">haskellPackages</span><span class="o">.</span><span class="nv">callPackage</span> <span class="sx">./default.nix</span> <span class="p">{</span> <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If you want to see which haskell packages are pinned for that commit, you can
go to <code class="language-plaintext highlighter-rouge">hackage-packages.nix</code> for that “rev”. So for the JSON above, that file would be <a href="https://raw.githubusercontent.com/NixOS/nixpkgs/8b92a4e600458c01ab0a72f2492eb7120e18f9bc/pkgs/development/haskell-modules/hackage-packages.nix">this link</a>.
Then you can either CTRL+F there or download that file and search locally to see
which version of a given dependency is pinned.</p>
<h2 id="setting-ghc-version">Setting GHC version</h2>
<p>But which version of GHC are we using? One way would be to search for <code class="language-plaintext highlighter-rouge">"base"</code> (with the quotes) and
backtrack to the version of GHC from that
(GHC <a href="https://downloads.haskell.org/~ghc/master/users-guide/8.6.1-notes.html#included-libraries">release notes</a> show included library versions).</p>
<p>Instead, my favorite way is via <code class="language-plaintext highlighter-rouge">nix repl</code>, made easier by the fact that we extracted our pinning function
to its own file. Since <code class="language-plaintext highlighter-rouge">haskellPackages</code> is an alias for <code class="language-plaintext highlighter-rouge">haskell.packages.<default-ghc-version></code>,
we can do the following:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nix repl
nix-repl> pkgs <span class="o">=</span> import ./pkgs-from-json.nix <span class="o">{</span> json <span class="o">=</span> ./nixos-18-03.json<span class="p">;</span> <span class="o">}</span>
nix-repl> pkgs.haskellPackages.ghc
«derivation /nix/store/djy5y2x23cpzksxpwc1zb3df9kq4y3lw-ghc-8.2.2.drv»
</code></pre></div></div>
<p>So, this nixpkgs is defaulting to ghc-8.2.2. What if we want to use a different GHC version?
First let’s see which versions we have available, this time using the auto-complete of
<code class="language-plaintext highlighter-rouge">nix repl</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nix-repl> pkgs.haskell.packages.ghc<<span class="nb">type </span>tab tab>
pkgs.haskell.packages.ghc7103 pkgs.haskell.packages.ghc822 pkgs.haskell.packages.ghcjs
pkgs.haskell.packages.ghc7103Binary pkgs.haskell.packages.ghc841 pkgs.haskell.packages.ghcjsHEAD
pkgs.haskell.packages.ghc802 pkgs.haskell.packages.ghc843
pkgs.haskell.packages.ghc821Binary pkgs.haskell.packages.ghcHEAD
</code></pre></div></div>
<p>If we want to use ghc843 instead, we would be tempted to replace <code class="language-plaintext highlighter-rouge">haskellPackages</code>
with <code class="language-plaintext highlighter-rouge">haskell.packages.ghc843</code>. Let’s try that just to see what happens:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nix-build release3.nix <span class="nt">--dry-run</span>
these derivations will be built:
/nix/store/l1yyi28qs37p64vr7s36rdz4fnv1gvcp-hscolour-1.24.2.drv
/nix/store/5r1w8v0d8cfbm5cpp5z5z2k1n1hjzj5z-semigroups-0.18.4.drv
/nix/store/6dpl86pga098dn0y5a7s74vl6wkzmq1p-transformers-compat-0.5.1.4.drv
/nix/store/2ag3fwj1r6x9sg8h8raiadyzkvkmnn8d-primitive-0.6.3.0.drv
/nix/store/qh092sigw95nazbaqna1g3flq4ll1r3p-random-1.1.drv
/nix/store/jc2kzlndcx4xc5y6gmjjqgmzglaqw1qa-tf-random-0.5.drv
/nix/store/d5a1b99r402jkmcq0cvqc1ghp23jps65-QuickCheck-2.10.1.drv
/nix/store/7pxj00jqs1qaw2vdgbl5sz8i5hh3cldp-setenv-0.1.1.3.drv
/nix/store/8i637r8ip2cc0f1sssgg01s1p86dvhy1-nanospec-0.2.2.drv
...
many many more
...
these paths will be fetched <span class="o">(</span>115.61 MiB download, 1289.39 MiB unpacked<span class="o">)</span>:
/nix/store/4wvwj5rqkj3kxwmbl10p3ridarfp1djl-ghc-8.4.3
/nix/store/vmrgjlzdvrk2aii6m0fn3rwajckrpwpx-ghc-8.4.3-doc
</code></pre></div></div>
<p>Whoa whoa whoa, we don’t want to BUILD all that – that would take forever. I’m not sure exactly why this happens, but
my guess is that the only haskell packages that are cached for this commit are
the ones listed in the raw.githubusercontent.com page I linked above. Since a new
GHC version is using a new base and core packages, chances are that package versions
are going to be resolved slightly differently, and you’ll need to build any that are
different.</p>
<p>In my opinion, a better way is to pick a Nix channel that has whatever GHC you
want in the default <code class="language-plaintext highlighter-rouge">haskellPackages</code>, since it will come with a set of packages that will work
well together and not require that you build so much yourself. In this case, nixos-18.09
has GHC 8.4.3 as the default, so we could use it instead by running:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nix-prefetch-git https://github.com/nixos/nixpkgs-channels.git refs/heads/nixos-18.09 <span class="o">></span> nixos-18-09.json
</code></pre></div></div>
<p>and pointing to this new <code class="language-plaintext highlighter-rouge">nixos-18-09.json</code> in your release file (<code class="language-plaintext highlighter-rouge">release4.nix</code>).</p>
<h2 id="overriding-packages">Overriding packages</h2>
<p>There are a few ways to override haskell packages but most are, surprisingly,
not very composable. For instance, if you were to try:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span>
<span class="nv">pkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="o"><</span><span class="nv">nixpkgs</span><span class="o">></span> <span class="p">{</span> <span class="p">};</span>
<span class="nv">overriddenPackages1</span> <span class="o">=</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">haskellPackages</span><span class="o">.</span><span class="nv">override</span> <span class="p">{</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">project1</span> <span class="o">=</span> <span class="nv">self</span><span class="o">.</span><span class="nv">callPackage</span> <span class="sx">./default.nix</span> <span class="p">{</span> <span class="p">};</span>
<span class="p">};</span>
<span class="p">};</span>
<span class="nv">overriddenPackages2</span> <span class="o">=</span> <span class="nv">overriddenPackages1</span><span class="o">.</span><span class="nv">override</span> <span class="p">{</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="c"># some more overrides</span>
<span class="p">};</span>
<span class="p">};</span>
<span class="kn">in</span>
<span class="p">{</span> <span class="nv">project1</span> <span class="o">=</span> <span class="nv">overriddenPackages2</span><span class="o">.</span><span class="nv">project1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>the execution would fail with <code class="language-plaintext highlighter-rouge">error: attribute 'project1' missing</code> at the final
line, since the second override cleared out what the first one set.</p>
<p>The best override methodology I’ve found is with the (slightly arcane) syntax described in<br />
<a href="https://github.com/NixOS/nixpkgs/issues/26561">this ticket</a>. Applying this, your
release file becomes <code class="language-plaintext highlighter-rouge">release5.nix</code>:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span>
<span class="nv">pinnedPkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="sx">./pkgs-from-json.nix</span> <span class="p">{</span> <span class="nv">json</span> <span class="o">=</span> <span class="sx">./nixos-18-09.json</span><span class="p">;</span> <span class="p">};</span>
<span class="nv">customHaskellPackages</span> <span class="o">=</span> <span class="nv">pinnedPkgs</span><span class="o">.</span><span class="nv">haskellPackages</span><span class="o">.</span><span class="nv">override</span> <span class="p">(</span><span class="nv">old</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">pinnedPkgs</span><span class="o">.</span><span class="nv">lib</span><span class="o">.</span><span class="nv">composeExtensions</span> <span class="p">(</span><span class="nv">old</span><span class="o">.</span><span class="nv">overrides</span> <span class="nv">or</span> <span class="p">(</span><span class="nv">_</span><span class="p">:</span> <span class="nv">_</span><span class="p">:</span> <span class="p">{}))</span> <span class="p">(</span><span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">project1</span> <span class="o">=</span> <span class="nv">self</span><span class="o">.</span><span class="nv">callPackage</span> <span class="sx">./default.nix</span> <span class="p">{</span> <span class="p">};</span>
<span class="c"># additional overrides go here</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="kn">in</span>
<span class="p">{</span> <span class="nv">project1</span> <span class="o">=</span> <span class="nv">customHaskellPackages</span><span class="o">.</span><span class="nv">project1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is using the built-in <code class="language-plaintext highlighter-rouge">composeExtensions</code> function to take the previous
overrides (or the empty set, {}) and merge it with whatever is inside the <code class="language-plaintext highlighter-rouge">self: super:</code> overlay.
In this change I also switched around where we define <code class="language-plaintext highlighter-rouge">project1</code>: now it is defined in our
haskellPackages with everything else and just returned at the end. I prefer
this approach since, from now on, our <code class="language-plaintext highlighter-rouge">project1</code> will be treated like any other haskell dependency.</p>
<h2 id="enabling-hoogle">Enabling Hoogle</h2>
<p>If you’re working on Haskell locally, you really should be using a local Hoogle
instance so that the package’s version you’re viewing for documentation matches
the version you’re using in your code. Fortunately Nix makes this quite easy,
especially once you set up your overrides as above. A standalone expression for
this looks like the following, call it <code class="language-plaintext highlighter-rouge">toggle-hoogle.nix</code>:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="c"># Library of functions to use, for composeExtensions. </span>
<span class="nv">lib</span> <span class="o">?</span> <span class="p">(</span><span class="kr">import</span> <span class="o"><</span><span class="nv">nixpkgs</span><span class="o">></span> <span class="p">{})</span><span class="o">.</span><span class="nv">pkgs</span><span class="o">.</span><span class="nv">lib</span>
<span class="c"># Whether or not hoogle should be enabled.</span>
<span class="p">,</span> <span class="nv">withHoogle</span>
<span class="c"># Input set of all haskell packages. A valid input would be:</span>
<span class="c"># (import <nixpkgs> {}).pkgs.haskellPackages</span>
<span class="p">,</span> <span class="nv">input</span>
<span class="p">}:</span>
<span class="k">if</span> <span class="nv">withHoogle</span>
<span class="k">then</span> <span class="nv">input</span><span class="o">.</span><span class="nv">override</span> <span class="p">(</span><span class="nv">old</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">lib</span><span class="o">.</span><span class="nv">composeExtensions</span> <span class="p">(</span><span class="nv">old</span><span class="o">.</span><span class="nv">overrides</span> <span class="nv">or</span> <span class="p">(</span><span class="nv">_</span><span class="p">:</span> <span class="nv">_</span><span class="p">:</span> <span class="p">{}))</span> <span class="p">(</span><span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">ghc</span> <span class="o">=</span> <span class="nv">super</span><span class="o">.</span><span class="nv">ghc</span> <span class="o">//</span> <span class="p">{</span> <span class="nv">withPackages</span> <span class="o">=</span> <span class="nv">super</span><span class="o">.</span><span class="nv">ghc</span><span class="o">.</span><span class="nv">withHoogle</span><span class="p">;</span> <span class="p">};</span>
<span class="nv">ghcWithPackages</span> <span class="o">=</span> <span class="nv">self</span><span class="o">.</span><span class="nv">ghc</span><span class="o">.</span><span class="nv">withPackages</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">})</span>
<span class="k">else</span> <span class="nv">input</span>
</code></pre></div></div>
<p>Then we can change our release file to call it with what we have so far, taking
an input to toggle this functionality. <code class="language-plaintext highlighter-rouge">release6.nix</code>:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">withHoogle</span> <span class="o">?</span> <span class="kc">false</span>
<span class="p">}:</span>
<span class="kd">let</span>
<span class="nv">pinnedPkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="sx">./pinnedPkgs.nix</span> <span class="p">{</span> <span class="nv">pinnedJsonFile</span> <span class="o">=</span> <span class="sx">./nixos-18-09.json</span><span class="p">;</span> <span class="p">};</span>
<span class="nv">customHaskellPackages</span> <span class="o">=</span> <span class="nv">pinnedPkgs</span><span class="o">.</span><span class="nv">haskellPackages</span><span class="o">.</span><span class="nv">override</span> <span class="p">(</span><span class="nv">old</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">overrides</span> <span class="o">=</span> <span class="nv">pinnedPkgs</span><span class="o">.</span><span class="nv">lib</span><span class="o">.</span><span class="nv">composeExtensions</span> <span class="p">(</span><span class="nv">old</span><span class="o">.</span><span class="nv">overrides</span> <span class="nv">or</span> <span class="p">(</span><span class="nv">_</span><span class="p">:</span> <span class="nv">_</span><span class="p">:</span> <span class="p">{}))</span> <span class="p">(</span><span class="nv">self</span><span class="p">:</span> <span class="nv">super</span><span class="p">:</span> <span class="p">{</span>
<span class="nv">project1</span> <span class="o">=</span> <span class="nv">self</span><span class="o">.</span><span class="nv">callPackage</span> <span class="sx">./default.nix</span> <span class="p">{</span> <span class="p">};</span>
<span class="c"># addditional overrides go here</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="nv">hoogleAugmentedPackages</span> <span class="o">=</span> <span class="kr">import</span> <span class="sx">./toggle-hoogle.nix</span> <span class="p">{</span> <span class="nv">withHoogle</span> <span class="o">=</span> <span class="nv">withHoogle</span><span class="p">;</span> <span class="nv">input</span> <span class="o">=</span> <span class="nv">customHaskellPackages</span><span class="p">;</span> <span class="p">};</span>
<span class="kn">in</span>
<span class="p">{</span> <span class="nv">project1</span> <span class="o">=</span> <span class="nv">hoogleAugmentedPackages</span><span class="o">.</span><span class="nv">project1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then, if we wanted to enter a shell where we had GHC packages built with documentation,
we could write a <code class="language-plaintext highlighter-rouge">shell.nix</code> with:</p>
<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span>
<span class="nv">projectDrv</span> <span class="o">=</span> <span class="p">(</span><span class="kr">import</span> <span class="sx">./release6.nix</span> <span class="p">{</span> <span class="nv">withHoogle</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="p">}</span> <span class="p">)</span><span class="o">.</span><span class="nv">project1</span><span class="p">;</span>
<span class="kn">in</span>
<span class="nv">projectDrv</span><span class="o">.</span><span class="nv">env</span>
</code></pre></div></div>
<p>And if we wanted to run Hoogle with these packages, it would just be a matter of
running the start command in this shell:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nix-shell <span class="nt">--run</span> <span class="s1">'hoogle server --port=8080 --local --haskell'</span>
</code></pre></div></div>
<h2 id="ending-notes">Ending Notes</h2>
<p>That’s all for this post, thanks for reading along this far! Again, if you want to see the final setup as this post left
off, check out <a href="https://github.com/cah6/haskell-nix-skeleton-1">this repository</a>. In the next post,
I’ll show how this basic project setup can be hooked up to an IDE.</p>
<p>If you’d like to comment on any part of this post, please do so in the associated
reddit post <a href="https://www.reddit.com/r/haskell/comments/9f3vrm/exploring_nix_haskell_part_1_project_setup/">here</a>! As my first formal blog post, any type of feedback is welcome!</p>I’ve been working a lot with Nix and Haskell lately, and I thought now would be a good time to pause and write about it before I either forget what I’ve learned or forget why I found it difficult in the first place.