1
1
# Motivation
2
2
3
- Why do we need another Haskell infrastructure for Nix?
4
-
5
- Doesn't nixpkgs
6
- provide a sufficiently good Haskell infrastructure already?
7
-
8
- Problems with
9
- the nixpkgs haskell infrastructure are covered in the following sections:
10
-
11
- ## Cross compilation
12
-
13
- ` nixpkgs ` has quite good support for cross compilation, however the
14
- Haskell infrastructure suffers from the fact that it heavily relies on
15
- the ` cabal2nix ` tool. ` cabal2nix ` (as well as tools that depend on it
16
- like ` stack2nix ` ) flattens the ` .cabal ` file at conversion time to a
17
- given os/arch/flags configuration. Thus to make cross compilation
18
- work with ` cabal2nix ` you will have to generate a separate ` nix `
19
- expression for each configuration. This becomes a major maintenance
20
- burden over time. Therefore the tooling that translates cabal files
21
- into nix-expressions for use with Haskell.nix retains the full
22
- conditional tree from the cabal file and exposes it to ` nix ` . In
23
- addition it will also expose the ` build-type ` value, which allows us
24
- to cache the ` Setup.hs ` for build-type simple and not have to rebuild
25
- it every time.
26
-
27
- ## Package sets
28
-
29
- We often rely on either package sets as provided by stackage or
30
- computed by cabal. ` nixpkgs ` provides its own curated package set
31
- which might or might not work for the projects we work on.
32
- ` stack2nix ` tries to solve this issue, here we go one step further and
33
- provide the infrastructure to allow any form of package set.
34
-
35
- ## Per component level control
36
-
37
- The Haskell builder in ` nixpkgs ` provides control over executables and
38
- libraries, to build a specific executable only however is rather
39
- tricky to do. This also leads to the cyclic dependencies issue.
40
-
41
- ## Cyclic dependencies
42
-
43
- The Haskell builder in ` nixpkgs ` exposes packages at the
44
- package level. If packages mutually depend on each other through tests
45
- and libraries, this leads to cyclic dependencies that nix can't resolve. By
46
- exposing the components to nix as separate derivations this will only
47
- occur if you have mutually dependent components.
48
-
49
- ## Build times
50
-
51
- The Haskell builder in nixpkgs builds a package sequentially, first the
52
- library then the executables and finally the tests. It then executes
53
- the tests before the package is considered done. The upshot of this
54
- is that packages are only considered done if the test-suites
55
- passed. The downside is that if you have to compile multiple packages
56
- the likelihood of them failing is low, you have unnecessarily
57
- serialized your build. In a more aggressive setting libraries could
58
- start building as early as their dependent libraries are built. Of
59
- course they will have to be invalidated later should the test-suites
60
- of their dependencies fail, but this way we can make use of parallel
61
- building. In an ideal scenario this will reduce build times close to
62
- the optimum.
63
-
64
- ## More logic in nix
3
+ ` haskell.nix ` is an infrastructure based on ` nix ` to build Haskell code.
4
+ It provides a way to build ` cabal-install ` and ` Stack ` based projects using ` nix ` ,
5
+ reading the ` cabal.project ` or ` stack.yaml ` files used by those tools, hence reducing
6
+ the amount of ` nix ` code that needs to be maintained and making it easy to continue
7
+ using ` cabal-install ` and ` Stack ` as well.
8
+
9
+ In the rest of this page we motivate ` haskell.nix ` by comparing it to:
10
+ - [ Stack] ( https://docs.haskellstack.org/en/stable/ ) and [ cabal-install] ( https://cabal.readthedocs.io/en/stable/ ) build tools
11
+ - [ nixpkgs] ( https://haskell4nix.readthedocs.io/ ) Haskell infrastructure for ` nix `
12
+
13
+
14
+ ## Comparison with ` Stack ` and ` cabal-install `
15
+
16
+ Using ` haskell.nix ` instead of ` Stack ` or ` cabal-install ` gives us:
17
+ - deterministic and hermetic builds
18
+ - distributed caching
19
+ - precise selection of the toolchain (GHC...) to use (which only ` Stack ` allows to some extent)
20
+ - precise selection of the native libraries to use (using ` nix ` ), if any
21
+
22
+ In addition, ` haskell.nix ` has better support for cross-compilation (e.g.
23
+ compiling Haskell code on Linux that will be run on Windows). It does this by
24
+ carefully distinguishing the GHC compiler for the build platform (used to
25
+ compile ` Cabal ` 's ` Setup.hs ` files for Linux in our example) and the GHC
26
+ compiler for the host platform (GHC cross-compiler targeting Windows in our
27
+ example).
28
+
29
+ By design ` haskell.nix ` reuses configuration files from other tools and converts
30
+ them into ` nix ` expressions:
31
+ - ` .cabal ` files
32
+ - ` Stack ` 's ` stack.yaml `
33
+ - ` cabal-install ` 's ` cabal.project ` ...
34
+
35
+ As such it doesn't require more work from you if your projects already build
36
+ with ` Stack ` or ` cabal-install ` .
37
+
38
+ ` haskell.nix ` can also be used to provide developer environments including
39
+ common Haskell tools: GHC, cabal-install, HLS (Haskell Language Server), hlint,
40
+ etc. With these environments, you don't need to use ` ghcup ` nor to pass programs
41
+ explicitly (e.g. as in ` cabal -w ghc-9.2.2 ` ). See [ devx] ( https://github.com/input-output-hk/devx ) .
42
+
43
+
44
+ ## Comparison with ` nixpkgs `
45
+
46
+ To properly compare with ` nixpkgs ` we need to get more into the technical details
47
+ of both solutions.
48
+
49
+ ### Cross compilation
50
+
51
+ ` haskell.nix ` has more maintainable support for cross-compilation (e.g.
52
+ compiling Haskell code on a Linux machine to produce a program that runs on
53
+ Windows).
54
+
55
+ Both ` nixpkgs ` and ` haskell.nix ` rely on tools to convert ` .cabal ` files into
56
+ ` nix ` expressions. ` .cabal ` files can contain conditionals (e.g. ` os(windows) ` ) to
57
+ conditionally build modules, pass flags to the compiler, etc.
58
+
59
+ The difference is that:
60
+ - ` nixpkgs ` generates a different ` nix ` expression for each os/arch/flags
61
+ configuration.
62
+ - ` haskell.nix ` generates a single ` nix ` expression that exposes the conditionals
63
+ to ` nix ` .
64
+
65
+ The drawback of the ` nixpkgs ` approach is that managing so many different ` nix `
66
+ expressions for a single ` .cabal ` file becomes a maintenance burden over time.
67
+
68
+ ### Performance: build-type
69
+
70
+ When ` haskell.nix ` converts a ` .cabal ` file into a ` nix ` expression, it keeps
71
+ track of the ` build-type ` value. All the ` .cabal ` files that use `build-type:
72
+ simple` reuse the same ` Setup` program that is built once and cached.
73
+
74
+ ### Dependencies: package sets
75
+
76
+ Not all Haskell packages work well together. As it is cumbersome to pinpoint
77
+ every package version explicitly, it is common to rely on curated sets of
78
+ packages: packages that are known to work well together to some extent (e.g.
79
+ Stackage snapshots).
80
+
81
+ - ` nixpkgs ` provides its own curated set of packages which might or might not
82
+ work for the project we work on.
83
+
84
+ - ` haskell.nix ` allows any form of package set.
85
+
86
+ First [ hackage.nix] ( https://github.com/input-output-hk/hackage.nix ) exposes the
87
+ ` nix ` expressions of every revision of every package from Hackage.
88
+
89
+ As the Hackage index is an ever growing repository of Haskell packages,
90
+ ` haskell.nix ` supports pinning the Hackage index to a specific revision
91
+ and letting Cabal's solver resolve the dependencies in a reproducible way.
92
+
93
+ An alternative is to start with a curated package set. For example,
94
+ [ stackage.nix] ( https://github.com/input-output-hk/stackage.nix ) exposes the
95
+ ` nix ` expressions of every Stackage Snapshot.
96
+
97
+ In addition, it is possible to explicitly specify a package version and
98
+ revision, or even to fetch its sources (e.g. using Git).
99
+
100
+ ### Granularity and performance: per component level control
101
+
102
+ Haskell packages can contain several * components* : libraries, executables,
103
+ testsuites...
104
+
105
+ - ` nixpkgs ` mostly considers package as a whole.
106
+ - ` haskell.nix ` uses component granularity for dependencies.
107
+
108
+ The ` nixpkgs ` approach leads to some issues:
109
+
110
+ - building only a specific component (e.g. an executable) in a package is tricky
111
+ to do
112
+
113
+ - dependencies of the different components are mixed up: this can lead to cyclic
114
+ dependencies that ` nix ` can't solve. For example, package ` unicode ` exposes
115
+ ` lib-unicode ` and ` test-unicode ` executable, where ` test-unicode ` depends on
116
+ ` lib-print ` from package ` print ` , which itself depends on ` lib-unicode ` .
117
+ Component-wise, dependencies aren't cyclic, however, package-wise, they are.
118
+
119
+ - build times: the Haskell builder in nixpkgs builds a package sequentially,
120
+ first the library then the executables and finally the tests. It then executes
121
+ the tests before the package is considered done. The upshot of this is that
122
+ packages are only considered done if the test-suites passed. The downside is
123
+ that if you have to compile multiple packages the likelihood of them failing
124
+ is low, you have unnecessarily serialized your build. In a more aggressive
125
+ setting libraries could start building as early as their dependent libraries
126
+ are built. Of course they will have to be invalidated later should the
127
+ test-suites of their dependencies fail, but this way we can make use of
128
+ parallel building. In an ideal scenario this will reduce build times close to
129
+ the optimum.
130
+
131
+ ### More logic in nix
65
132
66
133
The ` cabal2nix ` tool has a resolver that resolves system dependencies
67
134
and licenses to values in ` nixpkgs ` . This logic ends up being a simple
@@ -71,10 +138,10 @@ do into nix, and as such if changes are necessary (or needed to be
71
138
performed ad hoc) there is no need to rebuild the conversion tool and
72
139
subsequently mark every derived expression as out of date.
73
140
74
- ## Decoupling
141
+ ### Decoupling
75
142
76
- Finally, by treating Haskell .nix and nixpkgs as separate entities we
77
- can decouple the Haskell packages and infrastructure from the nixpkgs
143
+ Finally, by treating ` haskell .nix` and ` nixpkgs ` as separate entities we
144
+ can decouple the Haskell packages and infrastructure from the ` nixpkgs `
78
145
package set, and rely on it to provide us with system packages while
79
146
staying up to date with Haskell packages from hackage while retaining
80
147
a stable (or known to be good) nixpkgs revision.
0 commit comments