The following post is part of Spoke Proof. Tell your friends.
Haddock at your fingertips
Dash for macOS (and by the way welcome to the universe where we type macOS
with a straight face) made documentation cool by way of docsets, which are collections of indexed offline HTML files with table-of-contents and RSS-feed push publishing. Simple technologies strung together by glue and displayed in a pretty, keyboard-friendly app.
Thesis: for Haskell users, docset viewers like Dash are more or less mandatory. Most Haskell packages are a buffet of tiny functions ordered into deep hierarchies of modules. If we could get Dash and Haddock to be friends we could then
- Fuzzily search by function or module names;
- Click on Source links to look at function source, often just as illuminating as the documentation;
- Confidently write Haskell in the Brooklyn Botanical Garden, which is next to our apartment but lacks an internet connection.
This is well within our reach thanks to philopon/haddocset, a Haskell library and executable that eats package database configuration files and uses the information to mutate a docset.
Here are the steps:
-
Set up a working Haskell environment with Stack.
-
stack upgrade; stack new scratch; cd scratch; stack install haddocset
(we will want a scratch project to work in; as you will see you do not want the dependencies you will be building to overlap with your global project) -
Modify the dependencies section in
scratch.cabal
to list the packages you want to have included in your docset. I have an example further down on this page. -
alias -g .fast='--fast --ghc-options="-j +RTS -A1024m -n2m -RTS"'
-
stack build --haddock --stack-root $(pwd)/.root/ .fast
(we have to build in a new Stack root because Stack will helpfully skip over packages that have already been registered into your official root, even though they probably lack Haddocks) -
Make coffee. Putter around the household. Read. Check out /r/overwatch for the latest plays of the game. Wonder which character you will play next. Compulsively check the Slack channel you set up with your friends to see if anybody else is playing. When you close your eyes to sleep at night you see the Mercy’s ultimate indicator on your screen. “Heroes never die!” rings in your head at work. Should you go professional? You wonder how you will do when competitive play is released. Check your savings account to figure out how long you could be unemployed while you train to go professional. Consider moving to the suburbs in Somerville, Massachusetts to make your savings last longer.
-
Use zsh for its globbing syntax.
-
alias h="stack exec haddocset -- "
-
DOCSET=/tmp/scratch.docset
-
h -t $DOCSET create
-
ls -1 ~/.stack/snapshots/**/pkgdb/*.conf > confs
-
ls -1 .stack-work/**/pkgdb/*.conf >> confs
-
gsort -V confs
(use GNUsort(1)
to version-sort the lines so that e.g.foo/lts-6.12/bar/
comes afterfoo/lts-6.3/bar/
) -
stack list-dependencies | while read -r name version; do echo "$name-$version"; done | xargs -I{} zsh -c 'grep {} confs | tail -1' > deps
-
h -t $DOCSET add $(cat deps) -f
-
open $DOCSET # opens the docset in Dash on OS X
What the fuck are you doing
These pkgdb .conf
files live in your .stack-work/
and .root/
directories. Unfortunately there are multiple .conf
files, one for each version of a package that you have used on your machine. We want to grab all these confs and then do a mathematical set intersection with the list of transitive dependencies generated from our scratch.cabal
(see below). By transitively including all our dependencies we will ensure that inter-package links in the Haddock will work.
Furthermore: when we intersect we want the latest version of each package, which is why we gsort -V
and run xargs grep tail
.
We already have a finished turkey in the oven
In the end you should end up with a docset like mine, generated on June 16th 2016 with the scratch.cabal
below, aggressively zipped into a 33 MB download.
[snip]
library
hs-source-dirs: src
exposed-modules: Lib
build-depends: HTTP
, aeson
, array
, base >= 4.7 && < 5
, base-prelude
, bifunctors
, conduit
, esqueleto
, http-api-data
, lens
, lens-aeson
, mtl
, path-io
, persistent
, plan-b
, servant-client
, servant-server
, servant-swagger
, swagger2
, transformers
, uri-bytestring
, wreq
, zip
, vector
[snip]
You should of course modify the build-depends
block for the packages you use frequently. You can probably tell from mine that I am one of those heathen artless web programmers who deceive companies into paying me a market salary for the simple task of converting JSON over HTTP to SQL queries and back. By the way the NYC Originate office is hiring Haskell developers.
Release notes
-
2016 June: Original post; nothing to write home about
-
2016 August: Typos fixed;
--stack-root
discovered and written about;gsort -V
hack to work aroundlts-6.XX
version numbers