Haskell: Difference between revisions
imported>Tobias.bora No edit summary |
m Use lang=haskell for syntax highlight |
||
(31 intermediate revisions by 16 users not shown) | |||
Line 1: | Line 1: | ||
[https://www.haskell.org/ Haskell] is a statically-typed, purely functional programming language with strong support for type inference and lazy evaluation. | |||
For detailed information on Haskell support in Nixpkgs, refer to the official {{Nixpkgs Manual|name=Nixpkgs Manual: Chapter - Haskell|anchor=#haskell}}. | |||
== How to develop with Haskell and Nix == | == How to develop with Haskell and Nix == | ||
Line 21: | Line 9: | ||
[[File:haskell_choice.png]] | [[File:haskell_choice.png]] | ||
Note | {{Note|{{nixos:package|haskellPackages}} is a synonym of <code>haskell.packages.ghcXYZ</code> where <code>XYZ</code> is the current default version of GHC in nixpkgs. However you can use a different version by replacing <code>haskellPackages</code> with the wanted package, for instance use <code>haskell.compiler.ghc884</code> to use GHC 8.8.4. You can get the full list of available GHC versions using: | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="console"> | ||
$ nix-env -f "<nixpkgs>" -qaP -A haskell.compiler | $ nix-env -f "<nixpkgs>" -qaP -A haskell.compiler | ||
haskell.compiler.ghc8107 ghc-8.10.7 | haskell.compiler.ghc8107 ghc-8.10.7 | ||
Line 33: | Line 21: | ||
… | … | ||
</syntaxhighlight> | </syntaxhighlight> | ||
}} | |||
=== Scripting === | === Scripting === | ||
For simple scripts, you can directly use nix-shell to get a redistributable Haskell script that you can run on any Nix system with <code>./my-script.hs</code>: | For simple scripts, you can directly use nix-shell to get a redistributable Haskell script that you can run on any Nix system with <code>./my-script.hs</code>: | ||
< | <syntaxhighlight lang=haskell> | ||
#!/usr/bin/env nix-shell | #!/usr/bin/env nix-shell | ||
#!nix-shell --pure -i runghc -p "haskellPackages.ghcWithPackages (pkgs: [ pkgs.turtle ])" | #!nix-shell --pure -i runghc -p "haskellPackages.ghcWithPackages (pkgs: [ pkgs.turtle ])" | ||
main = do | main = do | ||
-- do stuff | |||
putStrLn "Hello world from a distributable Haskell script!" | putStrLn "Hello world from a distributable Haskell script!" | ||
</ | </syntaxhighlight> | ||
To write inline Haskell scripts in nix-code, refer to [[Nix-writers#Haskell]]. | |||
Read | Read [[#Overrides]] if some packages are broken. | ||
=== Directly using cabal (no nix caching/reproducibility) === | === Directly using cabal (no nix caching/reproducibility) === | ||
< | [https://www.haskell.org/cabal/ Cabal] is the basic Haskell tool used to configure builds and is internally used by all the Haskell's packaging methods (including stack and nix). If one does not care about the reproducibility/caching offered by nix, it is always possible to use cabal like in a normal system: | ||
<syntaxhighlight lang="console"> | |||
$ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ cabal-install ])" | $ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ cabal-install ])" | ||
$ cabal init | $ cabal init | ||
Line 57: | Line 49: | ||
Up to date | Up to date | ||
Hello, Haskell! | Hello, Haskell! | ||
</ | </syntaxhighlight> | ||
{{note|Some packages may need additional libraries/programs, notably <code>zlib</code>, you should be able to add them as additional programs in the nix-shell option.}} | |||
{{note|Since Cabal 2.0, cabal has acquired caching similar to nix (but not as powerful) and reproducibility (via the cabal.project file and the index-state option). See [https://cabal.readthedocs.io/en/latest/cabal-project-description-file.html#cfg-field-index-state] for more information.}} | |||
=== Using Stack (no nix caching) === | === Using Stack (no nix caching) === | ||
Similarly you can use | Similarly you can use [https://docs.haskellstack.org/en/stable/ Stack] that let you find the appropriate version of the libraries for you if you do not want the caching offered by nix (stack will build all the dependencies): | ||
< | <syntaxhighlight lang="console"> | ||
$ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ stack ])" | $ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ stack ])" | ||
$ stack new my-project | $ stack new my-project | ||
Line 70: | Line 63: | ||
$ stack build | $ stack build | ||
$ stack exec my-project-exe | $ stack exec my-project-exe | ||
</ | </syntaxhighlight> | ||
You can also use the features offered by stack to enable nix integration in order to use nix to install the non-haskell dependencies. You can read more [https://docs.haskellstack.org/en/stable/topics/nix_integration/ here]. | |||
If you want to package your program using stack in nix, you can actually use <code>haskell.lib.buildStackProject</code> that is a wrapper around <code>stdenv.mkDerivation</code> that will [https://github.com/NixOS/nixpkgs/blob/350fd0044447ae8712392c6b212a18bdf2433e71/pkgs/development/haskell-modules/generic-stack-builder.nix#L56 call <code>stack build</code> for you]… However because stack needs to download stuff you need to disable the sandbox using <code>nix-build --option sandbox false</code>. For instance if you want to compile a stack project that needs R, zeromq and zlib you can put the following into <code>default.nix</code>: | |||
<syntaxhighlight lang="nix"> | |||
with (import <nixpkgs> { }); | |||
haskell.lib.buildStackProject { | |||
name = "HaskellR"; | |||
buildInputs = [ R zeromq zlib ]; | |||
} | |||
</syntaxhighlight> | |||
{{note|For users of a stable version of NixOS there could be a problem where Stack tries to use a GHC version that is not yet in the given channel of Nixpkgs. Example at the time of writing: When using NixOS 23.05, Stack defaults to using the LTS-21.10 resolver, which uses <code>ghc-9.4.6</code>. However, the newest version of GHC in the 23.05 channel is <code>ghc-9.4.4</code>, thus Stack fails to execute some commands.<br/> | |||
As a solution, [https://docs.haskellstack.org/en/stable/yaml_configuration/#resolver-or-snapshot specify a resolver in your <code>stack.yaml</code>] file that uses a GHC version available for your channel. You can find a list of snapshots on https://www.stackage.org/snapshots. Or alternatively, set the resolver as a command line argument, which is required for running commands such as <code>stack new</code>.}} | |||
=== Using developPackage (use the nix packages set for haskell) === | === Using developPackage (use the nix packages set for haskell) === | ||
You can use | You can also use nix in place of stack to keep track of the dependencies in a reproducible way (note that while stack uses a solver to find a working set of dependencies, nix uses a fixed set of packages). Additionally you can benefit from the caching system offered by Nix. To that end, first create a cabal repository (nix also uses cabal internally): | ||
< | <syntaxhighlight lang="console"> | ||
$ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ cabal-install ])" --run "cabal init" | $ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ cabal-install ])" --run "cabal init" | ||
… | … | ||
</ | </syntaxhighlight> | ||
And create a file <code>default.nix</code> containing: | And create a file <code>default.nix</code> containing: | ||
Line 95: | Line 101: | ||
Then you can build and run the program using: | Then you can build and run the program using: | ||
< | <syntaxhighlight lang="console"> | ||
$ nix-build | $ nix-build | ||
$ ./result/bin/yourprogram | $ ./result/bin/yourprogram | ||
</ | </syntaxhighlight> | ||
or run a nix-shell to use the standard development tools provided by cabal: | or run a nix-shell to use the standard development tools provided by cabal: | ||
< | <syntaxhighlight lang="console"> | ||
$ nix- | $ nix-shell | ||
$ | $ cabal run | ||
</ | </syntaxhighlight> | ||
Nix will automatically read the <code>build-depends</code> field in the <code>*.cabal</code> file to get the name of the dependencies and use the haskell packages provided in the configured package set provided by nix. Note that some of the packages present in the nix repository are broken (for instance because a package requires an older version of a library while nix only provides a recent version). For this reason it may be necessary to override some packages present in the nix package set as described below using the <code>overrides</code> and <code>source-overrides</code> attribute. Note that the <code>source-overrides</code> attribute can also turn out to be useful to load local libraries: | Nix will automatically read the <code>build-depends</code> field in the <code>*.cabal</code> file to get the name of the dependencies and use the haskell packages provided in the configured package set provided by nix. Note that some of the packages present in the nix repository are broken (for instance because a package requires an older version of a library while nix only provides a recent version). For this reason it may be necessary to override some packages present in the nix package set as described below using the <code>overrides</code> and <code>source-overrides</code> attribute. Note that the <code>source-overrides</code> attribute can also turn out to be useful to load local libraries: | ||
Line 121: | Line 127: | ||
However as I understand I guess that you will not be able to enter the shell before `mylibrary` fully compiles… hence the need for `shellFor` to work simultaneously on multiple projects. | However as I understand I guess that you will not be able to enter the shell before `mylibrary` fully compiles… hence the need for `shellFor` to work simultaneously on multiple projects. | ||
Note that you may want to add tools needed either at compile time or a library at run time. For that, you can use the <code>modifier</code> field that is an arbitrary function to apply to the final haskell package (in particular you can apply the <code>overrideCabal</code> that we saw above). Notably, you can add nativeBuildInputs using [https://github.com/NixOS/nixpkgs/blob/0ba44a03f620806a2558a699dba143e6cf9858db/pkgs/development/haskell-modules/lib/compose.nix#L156 pkgs.haskell.lib.addBuildTools] and buildInputs using [https://github.com/NixOS/nixpkgs/blob/0ba44a03f620806a2558a699dba143e6cf9858db/pkgs/development/haskell-modules/lib/compose.nix#L159 pkgs.haskell.lib.addExtraLibraries] (for those of you that are curious to see how they are used in the final derivation, see [https://github.com/NixOS/nixpkgs/blob/0ba44a03f620806a2558a699dba143e6cf9858db/pkgs/development/haskell-modules/generic-builder.nix#L653 here]): | |||
<syntaxhighlight lang="nix"> | |||
let | |||
pkgs = import <nixpkgs> { }; # pin the channel to ensure reproducibility! | |||
in | |||
pkgs.haskellPackages.developPackage { | |||
root = ./.; | |||
modifier = drv: | |||
pkgs.haskell.lib.addBuildTools drv (with pkgs.haskellPackages; | |||
[ cabal-install | |||
ghcid | |||
]); | |||
} | |||
</syntaxhighlight> | |||
You can also get more details in [https://github.com/NixOS/nixpkgs/blob/0ba44a03f620806a2558a699dba143e6cf9858db/pkgs/development/haskell-modules/make-package-set.nix#L230 pkgs/development/haskell-modules/make-package-set.nix]. | |||
=== Using shellFor (multiple packages) === | === Using shellFor (multiple packages) === | ||
Line 127: | Line 149: | ||
<code>shellFor</code> is similar to <code>developPackage</code> but (slightly) more complicated to also allow you to develop multiples packages at the same time (similar to <code>cabal.project</code>). Note that contrary to <code>developPackage</code> I don't think that <code>shellFor</code> can output a derivation. | <code>shellFor</code> is similar to <code>developPackage</code> but (slightly) more complicated to also allow you to develop multiples packages at the same time (similar to <code>cabal.project</code>). Note that contrary to <code>developPackage</code> I don't think that <code>shellFor</code> can output a derivation. | ||
The idea is to first extend/override the set of haskell packages in order to add your projects as additional haskell packages (for instance using <code>haskellPackages.extend</code> and <code>packageSourceOverrides</code> that just need | The idea is to first extend/override the set of haskell packages in order to add your projects as additional haskell packages (for instance using <code>haskellPackages.extend</code> and <code>packageSourceOverrides</code> that just need the path of the project to compile it), and then to use <code>haskellPackages.shellFor {packages= p: [p.myproject1 p.myproject2]}</code> to create a shell with all wanted packages. | ||
For instance you can define your various projects in subfolders <code>./frontend</code> and <code>./backend</code> (you can use cabal init to create the content in each folder), then create a file <code>cabal.project</code> containing: | For instance you can define your various projects in subfolders <code>./frontend</code> and <code>./backend</code> (you can use cabal init to create the content in each folder), then create a file <code>cabal.project</code> containing: | ||
<syntaxhighlight> | <syntaxhighlight lang=text> | ||
packages: | packages: | ||
frontend/ | frontend/ | ||
Line 156: | Line 178: | ||
then you can use cabal to develop incrementally your projects using for instance: | then you can use cabal to develop incrementally your projects using for instance: | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="console"> | ||
$ nix-shell | $ nix-shell | ||
$ cabal new-build all | $ cabal new-build all | ||
Line 174: | Line 196: | ||
=== Using haskell.nix (for complex projects) === | === Using haskell.nix (for complex projects) === | ||
The [https://github.com/input-output-hk/haskell.nix haskell.nix] project allows you to have maximum flexibility (to create your own package set or use in teams with diverse people, some of them using stack, other using cabal, other using nix…). But this comes at the price of an additional | The [https://github.com/input-output-hk/haskell.nix haskell.nix] project allows you to have maximum flexibility (to create your own package set or use in teams with diverse people, some of them using stack, other using cabal, other using nix…). But this comes at the price of additional complexity. | ||
=== Using haskell-flake (flake-parts) === | |||
[https://community.flake.parts/haskell-flake haskell-flake] is a project that aims to simplify writing Nix for Haskell development through use of [[Flake Parts|flake-parts module system]]. It uses <code>callCabal2nix</code> and <code>shellFor</code> under the hood while exposing friendly module options API. For an overview of Flakes, see the [[Flakes]] wiki page. | |||
* For existing Haskell projects, initialize with: | |||
<syntaxhighlight lang="bash"> | |||
nix flake init -t github:srid/haskell-flake | |||
</syntaxhighlight> | |||
* For new Haskell projects, use the example template: | |||
<syntaxhighlight lang="bash"> | |||
mkdir example && cd ./example | |||
nix flake init -t github:srid/haskell-flake#example | |||
</syntaxhighlight> | |||
This command will generate a project template with additional configuration details, comments, and examples. Below is an example minimal flake definition for a simple project: | |||
{{file|flake.nix|nix| | |||
<nowiki> | |||
{ | |||
inputs = { | |||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; | |||
flake-parts.url = "github:hercules-ci/flake-parts"; | |||
haskell-flake.url = "github:srid/haskell-flake"; | |||
}; | |||
outputs = inputs@{ self, nixpkgs, flake-parts, ... }: | |||
flake-parts.lib.mkFlake { inherit inputs; } { | |||
systems = nixpkgs.lib.systems.flakeExposed; | |||
imports = [ inputs.haskell-flake.flakeModule ]; | |||
perSystem = { self', pkgs, ... }: { | |||
haskellProjects.default = { | |||
# Project configurations such as GHC version and package overrides are defined here | |||
# See: https://flake.parts/options/haskell-flake | |||
}; | |||
# haskell-flake doesn't set the default package, but you can do it here. | |||
packages.default = self'.packages.example; | |||
}; | |||
}; | |||
} | |||
</nowiki> | |||
}} | |||
Once configured, you can build and run the project with: | |||
<syntaxhighlight lang="console"> | |||
$ nix build # Build the project; the binary will be placed in ./result/bin | |||
$ ./result/bin/example | |||
$ nix run # Alternatively, run the default executable directly | |||
</syntaxhighlight> | |||
or enter a development shell to use the standard development tools provided by the flake: | |||
<syntaxhighlight lang="console"> | |||
$ nix develop | |||
$ cabal run | |||
</syntaxhighlight> | |||
The build process will use the <code>example.cabal</code> file and run the executable defined within it. A current limitation is that if your Cabal file contains multiple <code>executable</code> blocks, you can only assign one as the default package. This limitation also applies when using a <code>cabal.project</code>. To run a specific executable by name, use the following command <code>nix run .#another-example</code>. Below is an example cabal file defining two executables: | |||
{{file|example.cabal|haskell| | |||
<nowiki> | |||
executable example | |||
main-is: Main.hs | |||
... | |||
executable another-example | |||
main-is: AnotherMain.hs | |||
... | |||
</nowiki> | |||
}} | |||
==== Further reading ==== | |||
* [https://github.com/srid/haskell-template/tree/master Example Haskell project with a development environment] | |||
* [https://github.com/srid/haskell-multi-nix/tree/master Example cabal.project multi-package Haskell project] | |||
* [https://community.flake.parts/haskell-flake/start Getting started with haskell-flake]. | |||
* [https://community.flake.parts/haskell-flake/dependency Overriding dependencies in a haskell-flake] | |||
* [https://flake.parts/options/haskell-flake haskell-flake haskell-flake options reference] | |||
== Overrides == | == Overrides == | ||
Line 241: | Line 349: | ||
Note that <code>overrideCabal</code> takes as input the old package and the new attributes of the new package and outputs the new package. | Note that <code>overrideCabal</code> takes as input the old package and the new attributes of the new package and outputs the new package. To see the full list of parameters that can be overridden, you can refer to [https://github.com/NixOS/nixpkgs/blob/0ba44a03f620806a2558a699dba143e6cf9858db/pkgs/development/haskell-modules/generic-builder.nix#L13 this file]. | ||
Because some operations are very common, there exists some functions that call <code>overrideCabal</code> for you. For instance if you only want to disable checks and test suits for a package you can do <code>mypackage = pkgs.haskell.lib.dontCheck super.mypackage</code> and the above code also shows how to mark a package as unbroken. These functions are listed and documented in [https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/haskell-modules/lib/compose.nix pkgs/development/haskell-modules/lib/compose.nix]. | Because some operations are very common, there exists some functions that call <code>overrideCabal</code> for you. For instance if you only want to disable checks and test suits for a package you can do <code>mypackage = pkgs.haskell.lib.dontCheck super.mypackage</code> and the above code also shows how to mark a package as unbroken. These functions are listed and documented in [https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/haskell-modules/lib/compose.nix pkgs/development/haskell-modules/lib/compose.nix]. | ||
Line 269: | Line 377: | ||
== Limitations == | == Limitations == | ||
=== cabal2nix === | |||
When using the <code>cabal2nix</code> tool, Nix does not pull a cabal package by respecting the constraint specified in the cabal file (see [https://github.com/chrissound/Cabal2NixLimitationExample example]). Issue is discussed [https://stackoverflow.com/questions/57441156/pulling-in-specific-haskell-packages-cabal-dependencies-with-nix here]. You should be using <code>callCabal2nix</code> anyway. | |||
<code>callCabal2nix</code>, which is implicitly used for building Haskell projects, uses IFD.[https://github.com/NixOS/templates/issues/28][https://discourse.nixos.org/t/another-simple-flake-for-haskell-development/18164/6]. This means that since IFD is disabled by default in certain nix commands,[https://github.com/NixOS/nix/pull/5253] the following commands will be broken for Haskell projects whose flake output specifies multiple system attributes: | === IFD and Haskell === | ||
<code>callCabal2nix</code>, which is implicitly used for building Haskell projects, uses IFD. Refer to this [https://github.com/NixOS/templates/issues/28 github issue] and [https://discourse.nixos.org/t/another-simple-flake-for-haskell-development/18164/6 discourse thread] for additional context. This means that since IFD is disabled by default in certain nix commands,[https://github.com/NixOS/nix/pull/5253] the following commands will be broken for Haskell projects whose flake output specifies multiple system attributes: | |||
* <code>nix flake show</code> | * <code>nix flake show</code> | ||
* <code>nix flake check</code> | * <code>nix flake check</code> | ||
=== GHCup === | |||
[https://www.haskell.org/ghcup/ GHCup] does not work properly on NixOS out of the box. NixOS cannot run dynamically linked executables built for generic Linux environments due to its runtime linker setup. For details and a workaround, see [https://nix.dev/guides/faq#how-to-run-non-nix-executables nix.dev's explanation of stub-ld]. | |||
In most cases there is little reason to use GHCup when working within a Nix-based system, as Nixpkgs can achieve the same goals such as managing multiple GHC versions and other Haskell tooling. | |||
== FAQ and resources == | |||
* '''Official Docs:''' [https://nixos.org/manual/nixpkgs/unstable/#haskell '''The Haskell section in the nixpkgs manual'''] | |||
* [https://nixos.asia/en/nixify-haskell-nixpkgs '''Nixifying a Haskell project using nixpkgs'''] explains how to use Nix to package and develop Haskell projects using nothing but nixpkgs. | |||
* [https://github.com/mhwombat/nix-for-numbskulls/blob/78bcc186f79931c0e4a1e445e2f6b1f12f6d46be/Haskell/ss-haskell-dev.md '''Super-Simple Haskell Development with Nix'''] (and [https://discourse.nixos.org/t/super-simple-haskell-development-with-nix/14287/2 discussion] that provides interesting alternative methods together with there pro and cons) | |||
* [https://discourse.nixos.org/t/nix-haskell-development-2020/6170 '''Nix Haskell Development (2020)'''] | |||
* [https://discourse.nixos.org/t/haskellpackages-stm-containers-fails-to-build/5416/4 '''How are Haskell packages managed in nixpkgs?'''] | |||
* [https://www.youtube.com/watch?v=KLhkAEk8I20 '''How to fix broken Haskell packages?''' (video)] | |||
[[Category:Languages]] | [[Category:Languages]] | ||
[[Category: | [[Category:Haskell]] |