Tools:
My setup matches my personality and that is I always go for the bare minimum. I download all these tools using nixpkgs and I enable haskell-mode in doom-emacs. That’s it! I start writing Haskell.
Once I start writing Haskell I have another terminal open that runs ghcid. It has very fast development feedback loop, it’s how I know if my types are matching. I use typed-holes heavily so ghcid can tell me what types I need for a section of my code.
Here’s a contrived example
add :: Integer -> Integer -> Integer
= x `_whatdo` y add x y
This will be ghcid’s response in the repl
<interactive>:3:13: error:
• Found hole: _what :: Integer -> Integer -> Integer
Or perhaps ‘_what’ is mis-spelled, or not in scope
• In the expression: _what
In the expression: x `_what` y
In an equation for ‘add’: add x y = x `_what` y
• Relevant bindings include
y :: Integer (bound at <interactive>:3:7)
x :: Integer (bound at <interactive>:3:5)
add :: Integer -> Integer -> Integer (bound at <interactive>:3:1)
Valid hole fits include
add :: Integer -> Integer -> Integer (bound at <interactive>:3:1)
seq :: forall a b. a -> b -> b
with seq @Integer @Integer
(imported from ‘Prelude’
(and originally defined in ‘ghc-prim-0.5.3:GHC.Prim’))
(-) :: forall a. Num a => a -> a -> a
with (-) @Integer
(imported from ‘Prelude’ (and originally defined in ‘GHC.Num’))
asTypeOf :: forall a. a -> a -> a
with asTypeOf @Integer
(imported from ‘Prelude’ (and originally defined in ‘GHC.Base’))
const :: forall a b. a -> b -> a
with const @Integer @Integer
(imported from ‘Prelude’ (and originally defined in ‘GHC.Base’))
max :: forall a. Ord a => a -> a -> a
with max @Integer
(imported from ‘Prelude’
(and originally defined in ‘ghc-prim-0.5.3:GHC.Classes’))
(Some hole fits suppressed; use -fmax-valid-hole-fits=N or -fno-max-valid-hole-fits)
This tells me that I need a function that takes two Integer
that also outputs
an Integer
. This even tells me the “Relevant bindings” and “Valid hole fits”
which is amazing. ghcid helps as much as it can. This flow goes a very long
way. It starts getting hazy for me when it comes to lens errors,
but I’m sure if I use lens more often I’ll be able to decipher what ghcid
is telling me much quickly. This even works in the type level.
add :: _whatdo
= x + y add x y
ghcid repl will respond with
<interactive>:8:1: error:
• Couldn't match expected type ‘_whatdo’
with actual type ‘Integer -> Integer -> Integer’
‘_whatdo’ is a rigid type variable bound by
the type signature for:
add :: forall _whatdo. _whatdo
at <interactive>:7:1-14
• The equation(s) for ‘add’ have two arguments,
but its type ‘_whatdo’ has none
• Relevant bindings include
add :: _whatdo (bound at <interactive>:8:1)
ghcid is the reason I don’t use any IDE or any fancy editor plugins. I only have syntax highlighting and some formatting in my emacs.
When it comes to actually running my code I usually use cabal. After ghcid tells me my code is clear I run
cabal new-build
cabal new-run
Sometimes I use stack because of yesod. That’s mostly for work.
I’m aware of the holy war between stack, cabal, and nix, I hope this blog doesn’t get involved in that war. I use all of them. They all have a spot in my workflow.