Using hoogle in a haskell development environment on nix
Here's what my Nix Haskell dev environment looks like
in ~/.nixpkgs/config.nix
:
The environment helper function
First off, define a haskellEnvFun function for building Haskell environments:
packageOverrides = super: rec {
haskellEnvFun = { withHoogle ? false, compiler ? null, name }:
let hp = if compiler != null
then super.haskell.packages.${compiler}
else haskellPackages;
ghcWith = if withHoogle
then hp.ghcWithHoogle
else hp.ghcWithPackages;
in super.buildEnv {
name = name;
paths = [(ghcWith myHaskellPackages)];
};
Defining some environments
Call this function to define two environments: one for running the Hoogle builder on changes, and one without:
haskellEnvHoogle = haskellEnvFun {
name = "haskellEnvHoogle";
withHoogle = true;
};
haskellEnv = haskellEnvFun {
name = "haskellEnv";
withHoogle = false;
};
Packages
Define all the packages you want to use in your local Haskell dev environment:
myHaskellPackages = hp: with hp; [
Boolean
HTTP
HUnit
MissingH
QuickCheck
SafeSemaphore
Spock
aeson
async
attoparsec
bifunctors
blaze-builder
blaze-builder-conduit
blaze-builder-enumerator
blaze-html
blaze-markup
blaze-textual
cased
cassava
cereal
comonad
comonad-transformers
directory_1_2_4_0
dlist
dlist-instances
doctest
exceptions
fingertree
foldl
free
hamlet
hashable
hspec
hspec-expectations
html
http-client
http-date
http-types
io-memoize
keys
language-c
language-javascript
language-bash
lens
lens-action
lens-aeson
lens-datetime
lens-family
lens-family-core
lifted-async
lifted-base
linear
list-extras
list-t
logict
mime-mail
mime-types
mmorph
monad-control
monad-coroutine
monad-loops
monad-par
monad-par-extras
monad-stm
monadloc
mongoDB
monoid-extras
network
newtype
numbers
optparse-applicative
parsec
parsers
pcg-random
persistent
persistent-mongoDB
persistent-template
pipes
pipes-async
pipes-attoparsec
pipes-binary
pipes-bytestring
pipes-concurrency
pipes-csv
pipes-extras
pipes-group
pipes-http
pipes-mongodb
pipes-network
pipes-parse
pipes-safe
pipes-shell
pipes-text
posix-paths
postgresql-simple
pretty-show
profunctors
random
reducers
reflection
regex-applicative
regex-base
regex-compat
regex-posix
regular
relational-record
resourcet
retry
rex
safe
sbv
scotty
semigroupoids
semigroups
shake
shakespeare
shelly
simple-reflect
speculation
split
spoon
stm
stm-chans
stm-stats
streaming
streaming-bytestring
streaming-wai
strict
stringsearch
strptime
syb
system-fileio
system-filepath
tagged
taggy
taggy-lens
tar
tardis
tasty
tasty-hspec
tasty-hunit
tasty-quickcheck
tasty-smallcheck
temporary
test-framework
test-framework-hunit
text
text-format
time
tinytemplate
transformers
transformers-base
turtle
uniplate
unix-compat
unordered-containers
uuid
vector
void
wai
wai-conduit
warp
wreq
xhtml
yaml
zippers
zlib
];
Shell helpers
In your ~/.profile
define a couple bash functions to load those environments for convenience:
env-type () { envtype="$1" shift nix-shell -Q -p $envtype "$@" } haskell-env () { env-type "haskellEnv" "$@" } haskell-env-hoogle () { env-type "haskellEnvHoogle" "$@" }
Hoogle
Call haskell-env-hoogle
in your shell. This will build all of your packages + docs and load you into an environment with hoogle
in scope. At this point I usually type:
hoogle server --local -p 8080 &> /tmp/hoogle.log & disown
to launch a hoogle server the the background. Eventually I want to have a systemd service for this so that I can just nixos-rebuild to regen docs and launch the server automatically.
Emacs
For emacs I've set the haskell-hoogle-url
to http://localhost:8080/?hoogle=%s
, so that I can get local hoogle docs for keywords under my cursor. I use spacemacs so I just type , h h
for this functionality.
You can see my full nixpkgs config here: https://github.com/jb55/nix-files/blob/659798f2ca81fb7ad0cb5a29de576024ee16eef8/nixpkgs/config.nix#L20
Hope that helps.
haskellPackages.hoogleLocal
appears to be out of date; it doesn't exist anymore.
William Casarin's answer appears to be assuming you will have a single "haskell development environment" you use, rather than using nix-shell to have different dev environments for different projects.
What I've just figured out how to do instead is to write my shell.nix to override ghc.withPackages
and ghcWithPackages
to be ghc.withHoogle
, so that when nix-shell creates an environment with a GHC that knows about all the necessary packages it also creates a hoogle database that knows about the same packages.
Here's my shell.nix1:
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default", withHoogle ? true }:
let
inherit (nixpkgs) pkgs;
f = import ./default.nix;
packageSet = (
if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler}
);
haskellPackages = (
if withHoogle
then packageSet.override {
overrides = (self: super:
{
ghc = super.ghc // { withPackages = super.ghc.withHoogle; };
ghcWithPackages = self.ghc.withPackages;
}
);
}
else packageSet
);
drv = haskellPackages.callPackage f {};
in
if pkgs.lib.inNixShell then drv.env else drv
I'm newish to nix, but I believe this should be pretty much "project independent"; I can use cabal2nix . > default.nix
to generate a nix package from my cabal file when I change it, without having to touch shell.nix.
I haven't actually used this in real development yet, only a dummy project I was using to try to figure out how to get hoogle working in nix-shell.
1The skeleton of this was what cabal2nix --shell
spits out, with the project-specific guts removed and replaced with f = import ./default.nix
instead of embedding the nixified cabal package again.
Using @Ben's answer as reference, here is a diff on the required changes I needed to make to a cabal2nix --shell
file:
diff --git a/shell.nix b/shell.nix
index 540ade3..e207d6e 100644
--- a/shell.nix
+++ b/shell.nix
@@ -1,4 +1,4 @@
-{ nixpkgs ? import <nixpkgs> {}, compiler ? "default", doBenchmark ? false }:
+{ nixpkgs ? import <nixpkgs> {}, compiler ? "default", doBenchmark ? false , withHoogle ? true}:
let
@@ -21,10 +21,23 @@ let
license = stdenv.lib.licenses.bsd3;
};
- haskellPackages = if compiler == "default"
+ haskellPackages' = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
+ haskellPackages = (
+ if withHoogle
+ then haskellPackages'.override {
+ overrides = (self: super:
+ {
+ ghc = super.ghc // { withPackages = super.ghc.withHoogle; };
+ ghcWithPackages = self.ghc.withPackages;
+ }
+ );
+ }
+ else haskellPackages'
+ );
+
variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;
drv = variant (haskellPackages.callPackage f {});```