Haskell: Difference between revisions

Pigs (talk | contribs)
This flow chart is wrong or at least so much oversimplified that it is actively misleading
 
(2 intermediate revisions by one other user not shown)
Line 4: Line 4:


== How to develop with Haskell and Nix ==
== How to develop with Haskell and Nix ==
There are multiples ways to develop in Haskell on Nix depending on the simplicity of the project and on whether one want to benefit from the reproducibility offered by nix or not. Below is an image to help you in your choice:
[[File:haskell_choice.png]]


{{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:
{{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:
Line 25: Line 21:
=== 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 redistributable Haskell scripts on any Nix system, you can use a nix-shell shebang.
<syntaxhighlight lang=haskell>
<syntaxhighlight lang="haskell">#!/usr/bin/env nix-shell
#!/usr/bin/env nix-shell
#! nix-shell --pure -i runghc -p "ghc.withPackages (pkgs: [ pkgs.turtle ])"
#!nix-shell --pure -i runghc -p "haskellPackages.ghcWithPackages (pkgs: [ pkgs.turtle ])"
{-# LANGUAGE OverloadedStrings #-}
import Turtle


main = do
main = do
   -- do stuff
   -- do stuff
   putStrLn "Hello world from a distributable Haskell script!"
   echo "Hello world from a distributable Haskell script!"</syntaxhighlight>
</syntaxhighlight>
 
To remove the additional latency overhead of a nix-shell, add GHC to <code>environment.systemPackages</code> and call <code>runghc</code> in the shebang.<syntaxhighlight lang="nix">
  environment.systemPackages = with pkgs; [
    ...
    (ghc.withPackages (hsPkgs: with hsPkgs; [
      turtle      # Faster startup time with all external shell commands
      shh        # Piping operators and other goodies
      shh-extras  # Try shh as an interactive shell
      ...        # ...anything else you want!
    ])
  ];
</syntaxhighlight>Here's a basic example using the Shh module rather than Turtle, so it can use the pipe operator:<syntaxhighlight lang="haskell">
#!/usr/bin/env runghc
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE OverloadedStrings #-}
import Shh
 
-- $(loadEnv SearchPath) -- Loads entire PATH, may be slow
load SearchPath [ "dd", "sha512sum" ]


To write inline Haskell scripts in nix-code, refer to [[Nix-writers#Haskell]].
main = do
  dd "if=/dev/urandom" "bs=4K" "count=1" |> sha512sum
</syntaxhighlight>To write inline Haskell scripts in nix-code, refer to [[Nix-writers#Haskell]].


Read [[#Overrides]] if some packages are broken.
Read [[#Overrides]] if some packages are broken.
Line 43: Line 61:
[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:
[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">
<syntaxhighlight lang="console">
$  nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ cabal-install ])"
$  nix-shell -p "ghc.withPackages (pkgs: [ pkgs.cabal-install ])"
$ cabal init
$ cabal init
Line 58: Line 76:
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):
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">
<syntaxhighlight lang="console">
$ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ stack ])"
$ nix-shell -p "ghc.withPackages (pkgs: [ pkgs.stack ])"
$ stack new my-project
$ stack new my-project
$ cd my-project
$ cd my-project
Line 84: Line 102:
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):
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">
<syntaxhighlight lang="console">
$ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ cabal-install ])" --run "cabal init"
$ nix-shell -p "ghc.withPackages (pkgs: [ pkgs.cabal-install ])" --run "cabal init"
…  
…  
</syntaxhighlight>
</syntaxhighlight>