#+title: Create a new Haskell Project #+subtitle: Application Tutorial #+date: [2020-02-10 Mon] #+author: Yann Esposito #+EMAIL: yann@esposito.host #+keywords: Haskell programming functional tutorial #+DESCRIPTION: How to write Haskell application. #+OPTIONS: auto-id:t toc:t #+STARTUP: overview #+begin_notes Writing a Haskell application can be quite challenging. You must know about: - setup your coding environment - get the right compiler - use libraries - handle your Haskell tooling, editor/IDE - project directory structure and best practices - write tests - benchmarks - profiling - Code architecture - encode the data structure - manage state and effects This is both a manual and a tutorial. If you follow it, you should be familiar enough with Haskell to be able to write your own applications. I will focus on command line interfaces and REST APIs. #+end_notes * Haskell Environment Setup :PROPERTIES: :CUSTOM_ID: haskell-environment-setup :END: My no brainer solution for it: 1. Write this =shell.nix= file and launch =nix-shell=: #+begin_src nix :tangle shell.nix { nixpkgs ? import (fetchGit { name = "nixos-release-19.09"; url = "https://github.com/NixOS/nixpkgs"; # obtained via # git ls-remote https://github.com/nixos/nixpkgs master ref = "refs/heads/nixpkgs-19.09-darwin"; rev = "d5291756487d70bc336e33512a9baf9fa1788faf"; }) { config = { allowBroken = true; }; } }: let inherit (nixpkgs) pkgs; inherit (pkgs) haskellPackages; haskellDeps = ps: with ps; [ base protolude containers ]; hspkgs = haskellPackages; ghc = hspkgs.ghcWithPackages haskellDeps; nixPackages = [ ghc pkgs.gdb hspkgs.summoner hspkgs.summoner-tui haskellPackages.cabal-install haskellPackages.ghcid ]; in pkgs.stdenv.mkDerivation { name = "env"; buildInputs = nixPackages; shellHook = '' export PS1="\n\[[hs:\033[1;32m\]\W\[\033[0m\]]> " ''; } #+end_src 2. now launch =summon-tui= 3. add the following nix files: The first file to create is the one that will pin the versions of all your packages and libraries: #+caption: [[./my-app/nixpkgs.nix]] #+begin_src nix :tangle my-app/nixpkgs.nix :mkdirp t import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {} #+end_src The second file is the =default.nix= file: #+caption: [[./my-app/default.nix]] #+begin_src nix :tangle my-app/default.nix :mkdirp t { nixpkgs ? import ./nixpkgs.nix , compiler ? "default" , doBenchmark ? false }: let inherit (nixpkgs) pkgs; name = "my-app"; haskellPackages = pkgs.haskellPackages; variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id; drv = haskellPackages.callCabal2nix name ./. {}; in { my_project = drv; shell = haskellPackages.shellFor { # generate hoogle doc withHoogle = true; packages = p: [drv]; # packages dependencies (by default haskellPackages) buildInputs = with haskellPackages; [ hlint ghcid cabal-install cabal2nix hindent # # if you want to add some system lib like ncurses # # you could by writing it like: # pkgs.ncurses ]; # nice prompt for the nix-shell shellHook = '' export PS1="\n\[[${name}:\033[1;32m\]\W\[\033[0m\]]> " ''; }; } #+end_src ** Retrieve Compiler :PROPERTIES: :CUSTOM_ID: retrieve-compiler :END: ** Dependency Management :PROPERTIES: :CUSTOM_ID: dependency-management :END: ** Tooling :PROPERTIES: :CUSTOM_ID: tooling :END: * Haskell Project directoy structure :PROPERTIES: :CUSTOM_ID: haskell-project-directoy-structure :END: ** Tests :PROPERTIES: :CUSTOM_ID: tests :END: ** Benchmarks :PROPERTIES: :CUSTOM_ID: benchmarks :END: ** Profiling :PROPERTIES: :CUSTOM_ID: profiling :END: * Haskell Code Architecture :PROPERTIES: :CUSTOM_ID: haskell-code-architecture :END: ** Basic: IO :PROPERTIES: :CUSTOM_ID: basic--io :END: ** Easy: The Handle Pattern :PROPERTIES: :CUSTOM_ID: easy--the-handle-pattern :END: ** Advanced: MTL :PROPERTIES: :CUSTOM_ID: advanced--mtl :END: ** Expert: Free Monad :PROPERTIES: :CUSTOM_ID: expert--free-monad :END: