Setting up a Haskell Development Environment

Table of Contents

  • Index
  • Repository
  • 1 Haskell

    1.1 Overview

    Features

    • Pure Functional programming language
    • Strong Static Typed Language
    • Type Inference (The Haskell compiler deduces the types for you).
    • Lazy Evaluation (Delayed evaluation) by default
    • Data Immutability / Haskell has no variables
      • Values can be bound to a name and can only be assigned once.
      • Values can never change.
    • Haskell has not for-loop, while statements.
    • Algebraic Data types
    • Pattern Matching
    • Tail Recursions
    • Compiles to native code.

    Tool

       
    ghc - the Glasgow Haskell Compiler Transforms Haskell Source code .hs into native code.
    ghci Haskell Interactive Shell / Interpreter
    runghc Haskell Non Interactive Interpreter
    haddock Documentation tool for annotated Haskell source code
    cabal GHC Haskell Cabal building tool
    stack Haskell new package manager and building automation tool.
       

    Suffixes of file names for Haskell

    Extension Description  
    Source File    
    .hs Haskell source code; preprocess, compile  
    .lhs literate Haskell source; unlit, preprocess, compile  
         
    Compilation output    
    .hi Interface file; contains information about exported symbols  
    .hc intermediate C files  
    .x_o way x object files; common ways are: p, u, s  
    .x_hi way x interface files  
         

    1.2 Setting up a Haskell Development Environment

    1.2.1 Gettin Haskell through stack

    There are many ways to install Haskell like through Haskell Platform and Linux distributions packages. The most easier and reliable way is through stack tool which is a package manager and a build automation tool for Haskell because it can install and run multiple Haskell GHC versions without breaking each other or overwritten the already installed ghc in the system.

    Capabilities:

    • Install and run multiple versions of GHC avoiding the dependency hell and that each GHC version overwrites or breaks each other. All ghc versions are installed in the ~/.stack directory.
    • Can resolve and install all dependencies per project automatically.
    • Simple configuration file in YAML format stack.yaml.
    • Reliability: Fetch packages from a curated set of packages
    • Reproducible builds.
    • Ensures backward compatibility.

    Install stack

    See the instructions to install stack at https://www.haskellstack.org/

    Documentation

    1.3 Haskell stack tool

    1.3.1 Using stack without a project

    1.3.1.1 Overview

    Stack can be used exploratory programming without setting up a project by running any stack command outside a project directory (a directory without an stack.yaml file).

    LST Version / Resolver Haskell Version
    lts-8.3 GHC 8.0.2
    lts-7.19 GHC 8.0.1
    lts-6.30 GHC 7.10.3
    lts-3.22 GHC 7.10.2
    lts-2.22 GHC 7.8.4
    lts-0.7 GHC 7.8.3
       
    1.3.1.2 Installing packages globally

    To install a package globally run $ stack install <package> outside any project directory.

    $ cd ~
    
    $ stack install HUnit
    [1 of 2] Compiling Main             ( /home/arch/.stack/setup-exe-src/setup-mPHDZzAJ.hs, /home/arch/.stack/setup-exe-src/setup-mPHDZzAJ.o )
    [2 of 2] Compiling StackSetupShim   ( /home/arch/.stack/setup-exe-src/setup-shim-mPHDZzAJ.hs, /home/arch/.stack/setup-exe-src/setup-shim-mPHDZzAJ.o )
    Linking /home/arch/.stack/setup-exe-cache/x86_64-linux/tmp-Cabal-simple_mPHDZzAJ_1.24.0.0_ghc-8.0.1 ...
    HUnit-1.3.1.2: download
    HUnit-1.3.1.2: configure
    HUnit-1.3.1.2: build
    HUnit-1.3.1.2: copy/register
    
    1.3.1.3 Running Haskell REPL without a project

    Just run $ stack ghci outside of a project directory.

    $ stack ghci
    Configuring GHCi with the following packages: 
    GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
    Loaded GHCi configuration from /tmp/ghci6134/ghci-script
    Prelude> 
    Prelude> let f x = 10 * x 
    Prelude> f 20
    200
    Prelude> map f [1, 2, 3, 4, 5]
    [10,20,30,40,50]
    Prelude> 
    Prelude> :{
    Prelude| mysum :: Double -> Double -> Double 
    Prelude| mysum x y = x + y 
    Prelude| :}
    Prelude> 
    Prelude> :t mysum 
    mysum :: Double -> Double -> Double
    Prelude> 
    Prelude> mysum 10.23 20.23
    30.46
    Prelude>
    

    To pass command line options to ghci run:

    $ stack ghci --ghci-options="+RTS -M256m -K256m -RTS"
    

    or

    $ stack exec -- ghci +RTS -M256m -K256m -RTS
    
    1.3.1.4 Run a specific GHC version

    Run Haskell REPL ghci for LTS resolver 3.22 / ghc-7.10.2. GHC will be installed if it is not available.

    $ stack --resolver lts-3.22 --install-ghc ghci
    

    Run Haskell REPL ghci installing a specific version of GHC and installing packages HDBC, HDBC-sqlite3 and random automatically.

    $ stack --resolver lts-3.22 --install-ghc ghci --package HDBC --package HDBC-sqlite3 --package random
    HDBC-sqlite3-2.3.3.1: configure
    HDBC-sqlite3-2.3.3.1: build
    HDBC-sqlite3-2.3.3.1: copy/register
    Configuring GHCi with the following packages: 
    GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
    Prelude>
    
    1.3.1.5 Run a Haskell script with specific ghc version:
    $ stack --resolver lts-3.22 --install-ghc runghc haskell-script.hs
    
    1.3.1.6 Compile a haskell source code without a project
    $ stack ghc Main.hs Module1.hs Module2.hs
    

    or with command line options:

    $ stack exec -- ghc Main.hs Module1.hs Module2.hs -o myapp.bin
    
    1.3.1.7 Compile a sources without a project with a specific Haskell version
    $ stack --resolver lts-3.22 --install-ghc ghc Main.hs Module1.hs Module2.hs ...
    
    1.3.1.8 Misc
    1. Install and run a plot library
      $ pacman -S gnuplot
      
      1. Run
      $ stack --resolver lts-7.19 --install-ghc exec --package easyplot -- ghci
      

      LTS-7.19 - Always runs Haskell 8.0.1

      1. Plot something.
      $  stack --resolver lts-7.19 --install-ghc exec --package easyplot -- ghci
      GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
      Prelude> 
      Prelude> import Graphics.EasyPlot
      Prelude Graphics.EasyPlot> :set prompt "> "
      > :set prompt2 "- "
      > 
      >  plot X11 $ Gnuplot2D [Color Blue] [] "2**cos(x)"
      True
      
      >  plot X11 "x*y"
      True
      
    2. Install and run gtk GUI library
      1. Install gtk libraries for Arch Linux.
      $ sudo pacman -S gobject-introspection gobject-introspection-runtime gtksourceview3 webkitgtk webkit2gtk
      
      1. Run:

      Get ghci Version:

      $  stack --resolver lts-3.22 --install-ghc exec --package gtk -- runhaskell gui1.hs
      

      It installs gtk library and Haskell 7.10.2

      File gui1.hs

      import Control.Concurrent (forkIO, killThread)
      import Graphics.UI.Gtk
      
      main :: IO ()
      main = do
        initGUI
      
        {---------- Create GUI Widgets -------}
      
        -- Create a new Window
        w <- windowNew
      
        set w [windowTitle := "Hello gtk2hs"]
        windowSetDefaultSize w 300 400
      
         --    Add a button
        --
        b <- buttonNewWithLabel "click me"
        containerAdd w b
      
      
        {--------- Add Events ---------------}
        onClicked b (putStrLn "I was clicked !")
      
        {--------- Start GUI Loop --------}
        widgetShowAll w -- Refresh the Window to display the button.
        mainGUI
      
    3. Solve the problem: Ambiguous module name problem

      Sample code:

      File: gui1.hs

      import Graphics.UI.Gtk
      
      main :: IO ()    
      main = do
        initGUI  
        window  <- windowNew
        widgetShowAll window
        onDestroy window mainQuit
        mainGUI
      

      Problem description

      It happens because there are two packages gtk2hs installed gtk (gtk 2.0) and gtk3 (gtk 3.0).

      $ stack --resolver lts-3.22 runhaskell /tmp/gui1.hs 
      
      /tmp/gui1.hs:2:8:
          Ambiguous module name ‘Graphics.UI.Gtk’:
            it was found in multiple packages:
            gtk3-0.14.2@gtk3_AhgiKTeOdGE7p0vrO3qlnB gtk-0.13.9@gtk_DUp9k2RGwvV1yhb3dtjYiE
      

      Solution 1

      $ stack --resolver lts-3.22 exec -- ghc-pkg hide gtk3
      

      Running the sample code:

      $ stack --resolver lts-3.22 runhaskell /tmp/gui1.hs
      

      Solution 2

      An alternative solution is to use the language extension:

      {-# language PackageImports #-}
      import "gtk" Graphics.UI.Gtk
      
      main :: IO ()    
      main = do
        initGUI  
        window  <- windowNew
        widgetShowAll window
        onDestroy window mainQuit
        mainGUI
      

      For gtk3 package the code would be:

      {-# language PackageImports #-}
      
      import "gtk3" Graphics.UI.Gtk
      import "gtk3" Graphics.UI.Gtk.Builder
      import "gtk3" Graphics.UI.Gtk.Gdk.EventM
      
      import Control.Concurrent (forkIO, killThread)
      
      main :: IO ()
      main = do
        initGUI
      
        {---------- Create GUI Widgets -------}
      
        -- Create a new Window
        w <- windowNew
      
        set w [windowTitle := "Hello gtk2hs"]
        windowSetDefaultSize w 300 400
      
        --    Add a button
        --
        b <- buttonNewWithLabel "click me"
        containerAdd w b
      
        {--------- Add Events ---------------}
        onClicked b (putStrLn "I was clicked !")
      
        {--------- Start GUI Loop --------}
        widgetShowAll w -- Refresh the Window to display the button.
        mainGUI
      

    1.3.2 Build and manage projects with Stack

    Stack can install specific versions of packages and Haskell run-time per project in a isolated way avoiding the dependency hell problem, therefore it is possible to have multiple versions of Haskell without them breaking each other.

    See:

    1.3.3 Exploring Stack

    1.3.3.1 Where is ghci ?
    $ stack exec -- which ghci
    /home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghci
    
    1.3.3.2 Where is ghc ?
    $ stack exec -- which ghc
    /home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc
    
    1.3.3.3 Show stack search path
    $ stack path
    
    stack-root: /home/arch/.stack
    project-root: /home/arch/Documents/projects/zhserver.haskell
    config-location: /home/arch/Documents/projects/zhserver.haskell/stack.yaml
    bin-path: /home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/bin:/home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/arch/bin:/home/arch/.local/bin:/home/arch/opt/cling/bin:/home/arch/opt/cling2:/home/arch/opt/fsformatting:/home/arch/opt/gambit-4.8.4/bin:/home/arch/opt/jars:/home/arch/opt/java/bin:/home/arch/opt/jdk/bin:/home/arch/opt/jdk1.8.0_20/bin:/home/arch/opt/maven/bin:/home/arch/opt/scala-2.11.8/bin:/home/arch/opt/vivaldi
    programs: /home/arch/.stack/programs/x86_64-linux
    compiler-exe: /home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/bin/ghc
    compiler-bin: /home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/bin
    local-bin: /home/arch/.local/bin
    extra-include-dirs: 
    extra-library-dirs: 
    snapshot-pkg-db: /home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb
    local-pkg-db: /home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb
    global-pkg-db: /home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/lib/ghc-8.0.1/package.conf.d
    ghc-package-path: /home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb:/home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb:/home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/lib/ghc-8.0.1/package.conf.d
    snapshot-install-root: /home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1
    local-install-root: /home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1
    snapshot-doc-root: /home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/doc
    local-doc-root: /home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1/doc
    dist-dir: .stack-work/dist/x86_64-linux/Cabal-1.24.0.0
    local-hpc-root: /home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1/hpc
    local-bin-path: /home/arch/.local/bin
    ghc-paths: /home/arch/.stack/programs/x86_64-linux
    

    Way 1:

    $ echo $(stack exec -- bash -c "echo \$PATH") | tr ':' '\n' | grep stack
    
    /home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/lts-8.0/8.0.2/bin
    /home/arch/.stack/snapshots/x86_64-linux/lts-8.0/8.0.2/bin
    /home/arch/.stack/programs/x86_64-linux/ghc-8.0.2/bin
    
    1.3.3.4 Show all programs in stack search path
    $ echo $(stack exec -- bash -c "echo \$PATH") | tr ':' '\n' | grep stack | xargs ls -l
    ls: cannot access '/home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/lts-8.0/8.0.2/bin': No such file or directory
    /home/arch/.stack/programs/x86_64-linux/ghc-8.0.2/bin:
    total 72
    lrwxrwxrwx 1 arch arch     9 fev 16 10:58 ghc -> ghc-8.0.2
    -rwxr-xr-x 1 arch arch   418 fev 16 10:58 ghc-8.0.2
    lrwxrwxrwx 1 arch arch    10 fev 16 10:58 ghci -> ghci-8.0.2
    -rwxr-xr-x 1 arch arch   100 fev 16 10:58 ghci-8.0.2
    lrwxrwxrwx 1 arch arch    13 fev 16 10:58 ghc-pkg -> ghc-pkg-8.0.2
    -rwxr-xr-x 1 arch arch   450 fev 16 10:58 ghc-pkg-8.0.2
    lrwxrwxrwx 1 arch arch    17 fev 16 10:58 haddock -> haddock-ghc-8.0.2
    -rwxr-xr-x 1 arch arch   409 fev 16 10:58 haddock-ghc-8.0.2
    -rwxr-xr-x 1 arch arch 42907 fev 16 10:59 hp2ps
    -rwxr-xr-x 1 arch arch   380 fev 16 10:58 hpc
    -rwxr-xr-x 1 arch arch  1159 fev 16 10:58 hsc2hs
    lrwxrwxrwx 1 arch arch    12 fev 16 10:58 runghc -> runghc-8.0.2
    -rwxr-xr-x 1 arch arch   426 fev 16 10:58 runghc-8.0.2
    lrwxrwxrwx 1 arch arch     6 fev 16 10:58 runhaskell -> runghc
    
    /home/arch/.stack/snapshots/x86_64-linux/lts-8.0/8.0.2/bin:
    total 30164
    -rwxr-xr-x 1 arch arch 18205184 fev 16 11:44 cabal
    -rwxr-xr-x 1 arch arch 12677664 fev 16 11:20 smpl
    
    1.3.3.5 Show stack environment variables
    $ stack exec env 
    
    COLORTERM=truecolor
    DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-Ubv8WhFbAO,guid=676a06582bbad1b5d87c300b58a87182
    DESKTOP_SESSION=xfce
    DISPLAY=:0.0
    DOCKER_HOST=tcp://127.0.0.1:4243
    EDITOR=emacs -Q -nw --no-site -eval "(progn (setq  inhibit-startup-message t) (global-font-lock-mode t))"
    GDMSESSION=xfce
    GHC_PACKAGE_PATH=/home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb:/home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb:/home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/lib/ghc-8.0.1/package.conf.d
    GLADE_CATALOG_PATH=:
    GLADE_MODULE_PATH=:
    GLADE_PIXMAP_PATH=:
    GNOME_KEYRING_CONTROL=/home/arch/.cache/keyring-BEAWVY
    GTK_MODULES=canberra-gtk-module
    HASKELL_DIST_DIR=.stack-work/dist/x86_64-linux/Cabal-1.24.0.0
    HASKELL_PACKAGE_SANDBOX=/home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb
    HASKELL_PACKAGE_SANDBOXES=/home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb:/home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/pkgdb:
    HOME=/home/arch
    LANG=en_US.utf8
    LC_ADDRESS=pt_BR.UTF-8
    LC_IDENTIFICATION=pt_BR.UTF-8
    LC_MEASUREMENT=pt_BR.UTF-8
    LC_MONETARY=pt_BR.UTF-8
    LC_NAME=pt_BR.UTF-8
    LC_NUMERIC=pt_BR.UTF-8
    LC_PAPER=pt_BR.UTF-8
    LC_TELEPHONE=pt_BR.UTF-8
    LC_TIME=pt_BR.UTF-8
    LOGNAME=arch
    MAIL=/var/spool/mail/arch
    MOZ_PLUGIN_PATH=/usr/lib/mozilla/plugins
    PATH=/home/arch/Documents/projects/zhserver.haskell/.stack-work/install/x86_64-linux/ghc-8.0.1/8.0.1/bin:/home/arch/.stack/snapshots/x86_64-linux/ghc-8.0.1/8.0.1/bin:/home/arch/.stack/programs/x86_64-linux/ghc-8.0.1/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/arch/bin:/home/arch/.local/bin:/home/arch/opt/cling/bin:/home/arch/opt/cling2:/home/arch/opt/fsformatting:/home/arch/opt/gambit-4.8.4/bin:/home/arch/opt/jars:/home/arch/opt/java/bin:/home/arch/opt/jdk/bin:/home/arch/opt/jdk1.8.0_20/bin:/home/arch/opt/maven/bin:/home/arch/opt/scala-2.11.8/bin:/home/arch/opt/vivaldi
    PS1=
    \[\033[0;31m\]\u\[\033[0;36m\]@\[\033[0;34m\]\h \[\033[0;32m\]\A \[\033[0;36m\]\w\[\033[0;37m\]
    $ 
    PWD=/home/arch/Documents/projects/zhserver.haskell
    SHELL=/bin/bash
    SHLVL=2
    STACK_EXE=/usr/bin/stack
    TERM=xterm-256color
    USER=arch
    VISUAL=emacs -Q -nw --no-site -eval "(progn (setq  inhibit-startup-message t) (global-font-lock-mode t))"
    VTE_VERSION=4601
    WINDOWID=52514098
    XAUTHORITY=/home/arch/.Xauthority
    XDG_CONFIG_DIRS=/etc/xdg
    XDG_CURRENT_DESKTOP=XFCE
    XDG_DATA_DIRS=/usr/local/share:/usr/share
    XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/arch
    XDG_MENU_PREFIX=xfce-
    XDG_RUNTIME_DIR=/run/user/1000
    XDG_SEAT=seat0
    XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0
    XDG_SESSION_COOKIE=arch-pc-1487434114.151435-7060458
    XDG_SESSION_DESKTOP=xfce
    XDG_SESSION_PATH=/org/freedesktop/DisplayManager/Session0
    XDG_SESSION_TYPE=x11
    XDG_VTNR=7
    

    1.3.4 Some useful packages

    Basic Libraries

    $ stack install mtl random turtle conduit async network network uri
    

    Testing libraries:

    $ stack install HUnit QuickCheck
    

    Parsing and regex libraries:

    $ stack install attoparsec parsec regex-base regex-compat regex-posix
    

    1.4 GHCI Reference

    GHCI Interactive Shell

    Command Description
    :help Show help
    :load [haskell-source.hs] or :l src.hs Load Haskell source code
    :reload or :r Reload code after it was edited
    :type [expr] or :t [expr] Show the type of an expression
    :browse Gives the type signature of all functions in a module
    :set +s Print timing/memory stats after each evaluation
    :{ [code here ] :} Multiline code
    :set prompt ">" Change the prompt to ">"
    :cd [directory] change the current working directory to [directory]
    :! [shell command>] execute the shell command; :! pwd print the current directory
    :quit Quit the interpreter

    See also:

    Author: nobody

    Created: 2018-06-17 Sun 02:37

    Emacs 25.3.1 (Org mode 8.2.10)

    Validate