├── .github ├── ISSUE_TEMPLATE │ └── new_chapter.md └── workflows │ └── main.yml ├── .gitignore ├── .prettierrc ├── README.md ├── book.toml ├── default.nix ├── flake.lock ├── flake.nix ├── shell.nix └── src ├── SUMMARY.md ├── appendices ├── 01_stack_vs_cabal.md ├── 02_static_builds.md ├── 03_cabal_in_ci.md └── 04_transitioning_from_v1_to_v2.md ├── getting_fancy ├── 01_setting_up_a_cabal_project.md ├── 02_adding_doctests.md ├── 03_adding_local_hoogle.md ├── 04_flags_and_conditionals.md ├── 05_profiling_and_benchmarking.md ├── 06_foreign_libraries.md ├── 07_accessing_data_files.md ├── 08_hpack.md ├── 09_custom_setup.md ├── 10_backpack.md └── 11_nix_interop.md ├── leveling_up ├── 01_refactoring_and_re-use.md ├── 02_first_cabal_test-suite.md ├── 03_first_cabal_benchmark.md ├── 04_build_products_and_caching.md ├── 05_generating_documentation_with_haddock.md └── 06_uploading_package_to_hackage.md ├── new_to_cabal ├── 01_installing_cabal_and_ghc.md ├── 02_cabal_repl.md ├── 03_cabal_env.md ├── 04_initializing_a_cabal_repo.md ├── 05_basic_package_properties.md ├── 06_first_cabal_library.md ├── 07_first_cabal_executable.md └── 08_adding_dependencies.md └── preamble ├── 01_what_do_we_mean_when_we_say_cabal.md ├── 02_what_is_a_module_and_a_package.md ├── 03_what_is_hackage.md └── 04_how_to_install_software_with_cabal-install.md /.github/ISSUE_TEMPLATE/new_chapter.md: -------------------------------------------------------------------------------- 1 | --- 2 | labels: enhancement 3 | name: New Chapter 4 | title: "[SECTION NAME] - [CHAPTER NAME]" 5 | --- 6 | 7 | ## Brief Summary 8 | 9 | <-- Please summarize the content of the chapter, and perhaps provide some 10 | information about the intent (i.e. target audience, common issue, area of 11 | confusion etc.) !--> 12 | 13 | ## Prior Art 14 | 15 | <-- Please provide links to other guides or blogposts that cover this material 16 | as well, if applicable !--> 17 | 18 | ## Resources 19 | 20 | <-- Please link to any source code or cabal reference documentation that is 21 | applicable to this chapter !--> 22 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | format: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Prettier Check 15 | uses: creyD/prettier_action@v3.3 16 | with: 17 | dry: true 18 | prettier_options: --write --list-different **/*.{yaml,yml,md} 19 | deploy: 20 | needs: format 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - name: Setup mdbook 26 | uses: peaceiris/actions-mdbook@v1 27 | with: 28 | mdbook-version: "0.4.9" 29 | 30 | - run: mdbook build 31 | 32 | - name: Deploy 33 | uses: peaceiris/actions-gh-pages@v3 34 | if: github.ref == 'refs/heads/main' 35 | with: 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | publish_dir: ./book 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.pre-commit-config.yaml 2 | book 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "proseWrap": "always" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cabal User Guide 2 | 3 | ## Dependencies 4 | 5 | All dependencies are provided via nix. This project is defined as a flake so you 6 | can use `nix develop` to enter a shell if you have experimental features 7 | enabled. There is also `flake-compat` setup so a regular `nix-shell` should work 8 | if you don't want to use experimental features. 9 | 10 | If you don't want to use nix you can install these dependencies by themselves. 11 | It should be noted that the pre-commit-hooks are setup using nix, so if you want 12 | to not use nix it might be convenient to setup your own commit hooks, or you can 13 | run `prettier --write "./**/*.md"` on your own. CI will fail if markdown files 14 | are not correctly formatted! 15 | 16 | - [mdbook](https://rust-lang.github.io/mdBook/cli/index.html) 17 | - [prettier](https://prettier.io/) 18 | 19 | ### Getting Nix 20 | 21 | If you want to install nix please follow the instructions 22 | [here](https://nixos.org/download.html) 23 | 24 | If you want to enable experimental features (the `nix` command) you can find 25 | documentation about getting that setup 26 | [here](https://nixos.wiki/wiki/Nix_command) 27 | 28 | ## Running Locally 29 | 30 | This project is built with mdbook and they have 31 | [great documentation](https://rust-lang.github.io/mdBook/index.html). 32 | 33 | The main command for development is `mdbook serve` which will run the book 34 | locally on `localhost:3000`. 35 | 36 | ## Contributing 37 | 38 | Chapters can be edited in their corresponding markdown files (see 39 | [SUMMARY.md](./src/SUMMARY.md) for reference). To add a new chapter, add a link 40 | in SUMMARY.md and then create the corresponding markdown file. For more in depth 41 | instructions on adding content to an mdbook project see the 42 | [official docs](https://rust-lang.github.io/mdBook/format/summary.html) 43 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Cabal User Guide" 3 | authors = ["Jonathan Lorimer"] 4 | description = "A user friendly guide on how to get up and running with Cabal" 5 | language = "en" 6 | multilingual = false 7 | src = "src" 8 | 9 | [output.html] 10 | git-repository-url = "https://github.com/haskell/cabal-userguide" 11 | git-repository-icon = "fa-github" 12 | edit-url-template = "https://github.com/haskell/cabal-userguide/edit/main/{path}" 13 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | (import ( 2 | let 3 | lock = builtins.fromJSON (builtins.readFile ./flake.lock); 4 | in fetchTarball { 5 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 6 | sha256 = lock.nodes.flake-compat.locked.narHash; } 7 | ) { 8 | src = ./.; 9 | }).defaultNix 10 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1606424373, 7 | "narHash": "sha256-oq8d4//CJOrVj+EcOaSXvMebvuTkmBJuT5tzlfewUnQ=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "99f1c2157fba4bfe6211a321fd0ee43199025dbf", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-utils": { 20 | "locked": { 21 | "lastModified": 1623875721, 22 | "narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=", 23 | "owner": "numtide", 24 | "repo": "flake-utils", 25 | "rev": "f7e004a55b120c02ecb6219596820fcd32ca8772", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "numtide", 30 | "repo": "flake-utils", 31 | "type": "github" 32 | } 33 | }, 34 | "flake-utils_2": { 35 | "locked": { 36 | "lastModified": 1619345332, 37 | "narHash": "sha256-qHnQkEp1uklKTpx3MvKtY6xzgcqXDsz5nLilbbuL+3A=", 38 | "owner": "numtide", 39 | "repo": "flake-utils", 40 | "rev": "2ebf2558e5bf978c7fb8ea927dfaed8fefab2e28", 41 | "type": "github" 42 | }, 43 | "original": { 44 | "owner": "numtide", 45 | "repo": "flake-utils", 46 | "type": "github" 47 | } 48 | }, 49 | "nixpkgs": { 50 | "locked": { 51 | "lastModified": 1624024598, 52 | "narHash": "sha256-X++38oH5MKEmPW4/2WdMaHQvwJzO8pJfbnzMD7DbG1E=", 53 | "owner": "nixos", 54 | "repo": "nixpkgs", 55 | "rev": "33d42ad7cf2769ce6364ed4e52afa8e9d1439d58", 56 | "type": "github" 57 | }, 58 | "original": { 59 | "owner": "nixos", 60 | "ref": "nixos-unstable", 61 | "repo": "nixpkgs", 62 | "type": "github" 63 | } 64 | }, 65 | "nixpkgs_2": { 66 | "locked": { 67 | "lastModified": 1619531122, 68 | "narHash": "sha256-ovm5bo6PkZzNKh2YGXbRKYIjega0EjiEP0YDwyeXEYU=", 69 | "owner": "NixOS", 70 | "repo": "nixpkgs", 71 | "rev": "bb80d578e8ad3cb5a607f468ac00cc546d0396dc", 72 | "type": "github" 73 | }, 74 | "original": { 75 | "id": "nixpkgs", 76 | "type": "indirect" 77 | } 78 | }, 79 | "pre-commit-hooks": { 80 | "inputs": { 81 | "flake-utils": "flake-utils_2", 82 | "nixpkgs": "nixpkgs_2" 83 | }, 84 | "locked": { 85 | "lastModified": 1624280920, 86 | "narHash": "sha256-ILsUaNPHzZDlTGoUhSLhMSDkjPizTnoykwyeBYpPgtI=", 87 | "owner": "cachix", 88 | "repo": "pre-commit-hooks.nix", 89 | "rev": "628a319e1ee0f9e01d63a3dbe6c1681a177bc5f9", 90 | "type": "github" 91 | }, 92 | "original": { 93 | "owner": "cachix", 94 | "repo": "pre-commit-hooks.nix", 95 | "type": "github" 96 | } 97 | }, 98 | "root": { 99 | "inputs": { 100 | "flake-compat": "flake-compat", 101 | "flake-utils": "flake-utils", 102 | "nixpkgs": "nixpkgs", 103 | "pre-commit-hooks": "pre-commit-hooks" 104 | } 105 | } 106 | }, 107 | "root": "root", 108 | "version": 7 109 | } 110 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Cabal User Guide"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix"; 7 | flake-utils.url = "github:numtide/flake-utils"; 8 | flake-compat = { 9 | url = "github:edolstra/flake-compat"; 10 | flake = false; 11 | }; 12 | }; 13 | 14 | outputs = { self, nixpkgs, pre-commit-hooks, flake-utils, flake-compat }: 15 | flake-utils.lib.eachDefaultSystem (system: 16 | let pkgs = nixpkgs.legacyPackages.${system}; in 17 | { 18 | checks = { 19 | pre-commit-check = pre-commit-hooks.lib.${system}.run { 20 | src = ./.; 21 | hooks = { 22 | nixpkgs-fmt.enable = true; 23 | custom-prettier = { 24 | enable = true; 25 | name = "Custom Prettier"; 26 | entry = "${pkgs.nodePackages.prettier}/bin/prettier --write --list-different --ignore-unknown --config=\".prettierrc\""; 27 | files = "\\.(md|yml|yaml)$"; 28 | excludes = [ ".pre-commit-config.yaml" ]; 29 | language = "system"; 30 | }; 31 | }; 32 | }; 33 | }; 34 | devShell = pkgs.mkShell { 35 | inherit (self.checks.${system}.pre-commit-check) shellHook; 36 | buildInputs = with pkgs; [ mdbook nodePackages.prettier ]; 37 | }; 38 | } 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | (import ( 2 | let 3 | lock = builtins.fromJSON (builtins.readFile ./flake.lock); 4 | in fetchTarball { 5 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 6 | sha256 = lock.nodes.flake-compat.locked.narHash; } 7 | ) { 8 | src = ./.; 9 | }).shellNix 10 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | # Preamble 4 | 5 | - [What do we mean when we say cabal?]() 6 | - [What is a module, a component, and a package?](./preamble/02_what_is_a_module_and_a_package.md) 7 | - [What is hackage?]() 8 | - [How to install software with cabal-install]() 9 | 10 | # New To Cabal 11 | 12 | - [Installing cabal + GHC]() 13 | - [Cabal repl]() 14 | - [Cabal env]() 15 | - [Initializing a cabal repo]() 16 | - [Basic package properties]() 17 | - [First cabal library]() 18 | - [First cabal executable]() 19 | - [Adding dependencies]() 20 | 21 | # Leveling Up 22 | 23 | - [Refactoring and re-use]() 24 | - [First cabal test-suite]() 25 | - [First cabal benchmark]() 26 | - [Build products and caching]() 27 | - [Generating documentation with haddock]() 28 | - [Uploading package to hackage]() 29 | 30 | # Getting Fancy 31 | 32 | - [Setting up a cabal project]() 33 | - [Adding doctests]() 34 | - [Adding local hoogle]() 35 | - [Flags and conditionals]() 36 | - [Profiling and Benchmarking]() 37 | - [Foreign libraries]() 38 | - [Accessing data files]() 39 | - [Hpack]() 40 | - [Custom Setup]() 41 | - [Backpack]() 42 | - [Nix Interop]() 43 | 44 | # Appendices 45 | 46 | - [Stack vs cabal]() 47 | - [Static builds]() 48 | - [Cabal in CI]() 49 | - [Transitioning from v1 to v2]() 50 | -------------------------------------------------------------------------------- /src/appendices/01_stack_vs_cabal.md: -------------------------------------------------------------------------------- 1 | # Stack vs cabal 2 | -------------------------------------------------------------------------------- /src/appendices/02_static_builds.md: -------------------------------------------------------------------------------- 1 | # Static builds 2 | -------------------------------------------------------------------------------- /src/appendices/03_cabal_in_ci.md: -------------------------------------------------------------------------------- 1 | # Cabal in CI 2 | -------------------------------------------------------------------------------- /src/appendices/04_transitioning_from_v1_to_v2.md: -------------------------------------------------------------------------------- 1 | # Transitioning from v1 to v2 2 | -------------------------------------------------------------------------------- /src/getting_fancy/01_setting_up_a_cabal_project.md: -------------------------------------------------------------------------------- 1 | # Setting up a cabal project 2 | -------------------------------------------------------------------------------- /src/getting_fancy/02_adding_doctests.md: -------------------------------------------------------------------------------- 1 | # Adding doctests 2 | -------------------------------------------------------------------------------- /src/getting_fancy/03_adding_local_hoogle.md: -------------------------------------------------------------------------------- 1 | # Adding local hoogle 2 | -------------------------------------------------------------------------------- /src/getting_fancy/04_flags_and_conditionals.md: -------------------------------------------------------------------------------- 1 | # Flags and conditionals 2 | -------------------------------------------------------------------------------- /src/getting_fancy/05_profiling_and_benchmarking.md: -------------------------------------------------------------------------------- 1 | # Profiling And Benchmarking 2 | -------------------------------------------------------------------------------- /src/getting_fancy/06_foreign_libraries.md: -------------------------------------------------------------------------------- 1 | # Foreign libraries 2 | -------------------------------------------------------------------------------- /src/getting_fancy/07_accessing_data_files.md: -------------------------------------------------------------------------------- 1 | # Accessing data files 2 | -------------------------------------------------------------------------------- /src/getting_fancy/08_hpack.md: -------------------------------------------------------------------------------- 1 | # Hpack 2 | -------------------------------------------------------------------------------- /src/getting_fancy/09_custom_setup.md: -------------------------------------------------------------------------------- 1 | # Custom Setup 2 | -------------------------------------------------------------------------------- /src/getting_fancy/10_backpack.md: -------------------------------------------------------------------------------- 1 | # Backpack 2 | -------------------------------------------------------------------------------- /src/getting_fancy/11_nix_interop.md: -------------------------------------------------------------------------------- 1 | # Nix Interop 2 | -------------------------------------------------------------------------------- /src/leveling_up/01_refactoring_and_re-use.md: -------------------------------------------------------------------------------- 1 | # Refactoring and re-use 2 | -------------------------------------------------------------------------------- /src/leveling_up/02_first_cabal_test-suite.md: -------------------------------------------------------------------------------- 1 | # First cabal test-suite 2 | -------------------------------------------------------------------------------- /src/leveling_up/03_first_cabal_benchmark.md: -------------------------------------------------------------------------------- 1 | # First cabal benchmark 2 | -------------------------------------------------------------------------------- /src/leveling_up/04_build_products_and_caching.md: -------------------------------------------------------------------------------- 1 | # Build products and caching 2 | -------------------------------------------------------------------------------- /src/leveling_up/05_generating_documentation_with_haddock.md: -------------------------------------------------------------------------------- 1 | # Generating documentation with haddock 2 | -------------------------------------------------------------------------------- /src/leveling_up/06_uploading_package_to_hackage.md: -------------------------------------------------------------------------------- 1 | # Uploading package to hackage 2 | -------------------------------------------------------------------------------- /src/new_to_cabal/01_installing_cabal_and_ghc.md: -------------------------------------------------------------------------------- 1 | # Installing cabal + GHC 2 | 3 | ## ghcup 4 | 5 | While most package managers (notably [chocolatey](https://chocolatey.org/) for 6 | Windows) have some support for installing at least ghc (and sometimes cabal) the 7 | suggested method these days is `ghcup`. You should be able to run the curl 8 | command from [here](https://www.haskell.org/ghcup/) which should install the 9 | ghcup toolchain. Once you have ghcup installed you should be able to run 10 | commands via the tui `ghcup tui` or from the cli: 11 | 12 | > Note: these commands are taken directly from the ghcup repo. 13 | 14 | ``` 15 | # list available ghc/cabal versions 16 | ghcup list 17 | 18 | # install the recommended GHC version 19 | ghcup install ghc 20 | 21 | # install a specific GHC version 22 | ghcup install ghc 8.10.4 23 | 24 | # set the currently "active" GHC version 25 | ghcup set ghc 8.8.4 26 | 27 | # install cabal-install 28 | ghcup install cabal 29 | 30 | # update ghcup itself 31 | ghcup upgrade 32 | ``` 33 | 34 | Once you have `cabal-install` and `ghc` installed you should be able to check 35 | the versions in your terminal: 36 | 37 | ``` 38 | $ cabal --version 39 | cabal-install version 3.2.0.0 40 | compiled using version 3.2.0.0 of the Cabal library 41 | $ ghc --version 42 | The Glorious Glasgow Haskell Compilation System, version 8.8.4 43 | ``` 44 | 45 | You should have version 3 of cabal or later and greater than 8.8 for GHC. 46 | Although the GHC version shouldn't matter too much for this tutorial. 47 | 48 | You can test that cabal is working correctly by running the following commands 49 | 50 | ``` 51 | $ mkdir temp 52 | $ cd temp 53 | $ cabal init 54 | 55 | Guessing dependencies... 56 | 57 | Generating LICENSE... 58 | Warning: unknown license type, you must put a copy in LICENSE yourself. 59 | Generating Setup.hs... 60 | Generating CHANGELOG.md... 61 | Generating Main.hs... 62 | Generating temp.cabal... 63 | 64 | Warning: no synopsis given. You should edit the .cabal file and add one. 65 | You may want to edit the .cabal file and add a Description field. 66 | 67 | $ ls 68 | CHANGELOG.md Main.hs Setup.hs temp.cabal 69 | ``` 70 | 71 | If everything looks good, then you are ready to proceed to the rest of the 72 | guide! 73 | -------------------------------------------------------------------------------- /src/new_to_cabal/02_cabal_repl.md: -------------------------------------------------------------------------------- 1 | # Cabal repl 2 | -------------------------------------------------------------------------------- /src/new_to_cabal/03_cabal_env.md: -------------------------------------------------------------------------------- 1 | # Cabal env 2 | -------------------------------------------------------------------------------- /src/new_to_cabal/04_initializing_a_cabal_repo.md: -------------------------------------------------------------------------------- 1 | # Initializing a cabal repo 2 | -------------------------------------------------------------------------------- /src/new_to_cabal/05_basic_package_properties.md: -------------------------------------------------------------------------------- 1 | # Basic package properties 2 | -------------------------------------------------------------------------------- /src/new_to_cabal/06_first_cabal_library.md: -------------------------------------------------------------------------------- 1 | # First cabal library 2 | -------------------------------------------------------------------------------- /src/new_to_cabal/07_first_cabal_executable.md: -------------------------------------------------------------------------------- 1 | # First cabal executable 2 | -------------------------------------------------------------------------------- /src/new_to_cabal/08_adding_dependencies.md: -------------------------------------------------------------------------------- 1 | # Adding dependencies 2 | -------------------------------------------------------------------------------- /src/preamble/01_what_do_we_mean_when_we_say_cabal.md: -------------------------------------------------------------------------------- 1 | # What do we mean when we say cabal? 2 | -------------------------------------------------------------------------------- /src/preamble/02_what_is_a_module_and_a_package.md: -------------------------------------------------------------------------------- 1 | # What is a module, a component, and a package? 2 | 3 | ## TL;DR 4 | 5 | There is an ongoing initiative to standardize the terminology we use to refer to 6 | the hierarchy of collections of code in the Haskell community. If you are 7 | interested in reading more about this you can find a more thorough treatment 8 | [here](https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/compiler/units#background). 9 | 10 | The most significant concepts in this chapter all refer to ways of dividing and 11 | aggregating code. They form a hierarchy, and are somewhat defined in terms of 12 | each other. Here is a list of them in order from smallest to largest: 13 | 14 | 1. **Module** - The smallest unit of code defined by GHC and the Haskell 15 | language specification. A module always coincides with a file and cabal 16 | requires that the module name corresponds to the filename. 17 | 2. **Component** - A component is a collection of modules. These can be a 18 | `library`, `executable`, `test-suite`, and `benchmark`. 19 | 3. **Package** - A package is a collection of components, identified by a 20 | `.cabal` file. A package is **the** unit of distribution in the 21 | Haskell ecosystem, everything on Hackage is a package. A potential point of 22 | confusion is that there are such things as GHC packages, these are in fact 23 | closer to compiled components (there is more information on this in the link 24 | above). 25 | 4. **Project** - A project is a grouping of several related packages, denoted by 26 | a `cabal.project` file in the root directory. 27 | 28 | Projects are not covered here, but they do have their own 29 | [chapter](../getting_fancy/01_setting_up_a_cabal_project.md) 30 | 31 | ## What is a module? 32 | 33 | > This section takes inspiration from the introduction to a paper on the formal 34 | > specification of the Haskell module system. While the paper goes into the deep 35 | > end of things fairly quickly the introduction is approachable. I encourage you 36 | > to give it a look 37 | > [here](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.113.4699&rep=rep1&type=pdf) 38 | 39 | Modules are a common feature in most programming languages, and tend to serve 40 | three purposes: 41 | 42 | - Namespace 43 | - Abstraction 44 | - Separate Compilation 45 | 46 | Here are some examples of how modules can be used to control the entities they 47 | expose, and how import syntax can be used to control what is brought into scope. 48 | 49 | Modules can export all of their entities. 50 | 51 | ```haskell 52 | module ExportAll where 53 | 54 | id :: a -> a 55 | id a = a 56 | 57 | compose :: (b -> c) -> (a -> b) -> (a -> c) 58 | compose f g = \a -> f (g a) 59 | ``` 60 | 61 | They can export a subset of entities. 62 | 63 | ```haskell 64 | module Identity ( 65 | Identity(..), 66 | id 67 | ) where 68 | 69 | data Identity a = Identity a 70 | 71 | id :: a -> a 72 | id a = a 73 | 74 | compose :: (b -> c) -> (a -> b) -> (a -> c) 75 | compose f g = \a -> f (g a) 76 | ``` 77 | 78 | They can also explicitly export all of their entities. 79 | 80 | ```haskell 81 | module Composition ( 82 | compose 83 | ) where 84 | 85 | compose :: (b -> c) -> (a -> b) -> (a -> c) 86 | compose f g = \a -> f (g a) 87 | ``` 88 | 89 | Imports of modules can be qualified with a prefix. 90 | 91 | ```haskell 92 | import qualified Identity 93 | import qualified Composition as C 94 | 95 | identity :: a -> a 96 | identity = C.compose Identity.id Identity.id 97 | ``` 98 | 99 | Imports can explicitly describe a few entities to bring into scope. 100 | 101 | ```haskell 102 | import Identity (id) 103 | import Composition (compose) 104 | 105 | identity :: a -> a 106 | identity = compose id id 107 | ``` 108 | 109 | Imports can explicitly hide a few entities and bring the rest into scope. 110 | 111 | ```haskell 112 | import ExportAll hiding (compose) 113 | import Composition (compose) 114 | 115 | identity :: a -> a 116 | identity = compose id id 117 | ``` 118 | 119 | If you want to learn more about Haskell modules and their syntax, then the 120 | [haskell2010 report](https://www.haskell.org/onlinereport/haskell2010/haskellch5.html) 121 | is a good place to start. 122 | 123 | Alright, back to the three primary features of a module, lets look at how they 124 | manifest in Haskell. 125 | 126 | #### Namespace 127 | 128 | At its most basic, a module is a namespace for collecting language specific 129 | primitives. In Haskell these are types, functions, and typeclasses (referred to 130 | as entities). This is by far the most common use case (as opposed to abstraction 131 | or trying to manipulate recompilation) in a Haskell application. Namespaces 132 | prevent the collision of homophonous entities, but regardless of collisions, 133 | they are often used to create semantic divisions in the structure of code 134 | (usually in a way that reflects domain specific concepts). 135 | 136 | It is good practice to make heavy use of namespaces; an 80 line file is much 137 | more approachable, and easy to take in at a glance, than a 1000 line file. 138 | 139 | #### Abstraction 140 | 141 | First off, what is an abstraction? It is effectively a separation of the 142 | external API from the internal implementation. One can accomplish this with 143 | modules by exporting a subset of entities, thus making the external API smaller 144 | and hiding some of the internals. 145 | 146 | The ML family of languages (of which Haskell is a descendant) in particular are 147 | known for having a very robust 148 | [module system](https://jozefg.bitbucket.io/posts/2017-01-08-modules.html), one 149 | which is very good at creating expressive abstractions. While Haskell is related 150 | to the ML family it does not have support for ML style modules, OCaml is an 151 | example of a modern language that does have full blown ML modules. There is an 152 | attempt at adding partial support for ML modules to Haskell, the project is 153 | called [backpack](../getting_fancy/10_backpack.md) but that's a slightly more 154 | advanced topic. 155 | 156 | Unlike ML modules, Haskell modules are not first class, they cannot be passed 157 | around as values; Haskell modules are bound to a single file. They can however, 158 | still provide a level of abstraction by limiting exports and creating opaque 159 | types. 160 | 161 | ```haskell 162 | module Money ( 163 | -- Notice that the constructor of PositiveDollar is not exported below 164 | PositiveDollar, 165 | -- PositiveDollar(..), <- this would export the constructors as well 166 | mkPositiveDollar 167 | ) where 168 | 169 | newtype PositiveDollar = PositiveDollar { unPositiveDollar :: Int } 170 | 171 | mkPositiveDollar :: Int -> Maybe PositiveDollar 172 | mkPositiveDollar n 173 | | n >= 0 = Just . PositiveDollar $ n 174 | | otherwise = Nothing 175 | ``` 176 | 177 | > Note: If you want to learn more about opaque types and their use cases this 178 | > [resource](https://www.cs.auckland.ac.nz/references/haskell/haskell-intro-html/modules.html) 179 | > is fantastic 180 | 181 | Now a consumer of the `Money` module can only construct a `PositiveDollar` by 182 | calling `mkPositiveDollar`, which ensures PositiveDollar can only ever be 183 | created from a non-negative `Int`. Handling negatives has been abstracted away 184 | from the end user! 185 | 186 | #### Separate Compilation 187 | 188 | Module separation can also impact compile times. Each module keeps track of the 189 | fingerprint (A unique identifier, in new versions of GHC it is a hash of the 190 | contents of the file) of the modules it depends on. If you change a module that 191 | is depended upon by other modules, you will need to recompile everything 192 | downstream of your change. This means that modules, when imported frequently, 193 | can trigger a large chain of recompilations. 194 | 195 | ``` 196 | 197 | Main.hs 198 | / | \ 199 | / / \ \ 200 | A B C D 201 | \ \ / / 202 | CustomPrelude.hs 203 | 204 | ``` 205 | 206 | > Note: Sometimes haskell projects will define a `CustomPrelude.hs` or 207 | > `Import.hs` module to reduce the boiler plate of importing common modules / 208 | > packages. While this isn't necessarily bad, it is important to understand the 209 | > impact it can have on compilation times! 210 | 211 | In the diagram above, we can imagine the dependency graph flowing upwards. That 212 | means that `Main.hs` imports `A`, `B`, `C`, and `D`, and all of the lettered 213 | modules depend on `CustomPrelude.hs`. Any change to the lettered modules will 214 | only require a recompilation of the changed module and `Main.hs`. However, if we 215 | make a change to `CustomPrelude.hs` then every single module needs to be 216 | recompiled. 217 | 218 | We could do something like split up `CustomPrelude.hs` into a module that is 219 | depended upon by `A` and `B` and another that is depended upon by `C` and `D`. 220 | This means that changes in `PreludeAB.hs` now only recompile `A` and `B` and the 221 | same for `PreludeCD.hs` and `C` and `D`. In this way we reduce overall compile 222 | times by making it clear to GHC, at the module level, which code depends on 223 | what. GHC will use this information to see what work it has already done that is 224 | still valid, and what needs to be re-computed. 225 | 226 | ``` 227 | 228 | Main.hs 229 | / | \ 230 | / / \ \ 231 | A B C D 232 | / / \ \ 233 | PreludeAB.hs PreludeCD.hs 234 | 235 | ``` 236 | 237 | > Note: Main.hs has to be recompiled every time, regardless, so it does help if 238 | > you can move code that doesn't change frequently down the dependency graph. 239 | 240 | For a deeper exposition of module recompilation the 241 | [GHC docs](https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/compiler/recompilation-avoidance) 242 | are a great resource. 243 | 244 | A crucial point to understand here is that `GHC` is responsible for this 245 | recompilation logic, and a modules capabilities are defined in the Haskell 246 | language specification. Why is it important for us, as cabal users, to 247 | understand the module dependency graph, and how to use modules in general? 248 | 249 | The answer is two-fold: 250 | 251 | 1. We will often interface with GHC's recompilation functionality through 252 | `cabal` with commands like `cabal build` and `cabal repl`. 253 | 2. The module is the smallest unit of concern within cabal, and therefore it is 254 | worthwhile to understand its meaning, use cases, and impact on our codebase. 255 | 256 | ## What is a Component? 257 | 258 | If a module is the construct for managing an internal dependency, then 259 | components are the construct for managing external dependencies. Components 260 | aggregate all of our internal dependencies to be exposed externally, and 261 | components also declare all of the external packages that we depend on so that 262 | they can be made available as modules within our code. 263 | 264 | When arranging components Cabal needs to be aware of all the modules that we 265 | would like to expose externally. Now _what_ we choose to expose is up to us. 266 | Sometimes we just want to write an application, in which case we don't need to 267 | expose any modules, just the `main` entrypoint that gets called when we run our 268 | executable. Sometimes we want to write a library for others to use, and we want 269 | to liberally expose everything. Sometimes we want to write a library with some 270 | nasty internal machinery which we would prefer to keep hidden, and only expose 271 | the clean external api. All of these use cases can be expressed using 272 | components. 273 | 274 | Components are represented by top level declarations within a `.cabal` file. 275 | There are four keywords (corresponding to the four kinds of components); 276 | `library`, `executable`, `test-suite`, and `benchmark`. There can be multiple 277 | components of the same type declared in the same `.cabal` file. Components 278 | should all be given a name, except for libraries, which can be unnamed. A 279 | **library** exposes several modules which are intended to be used by other 280 | packages. An **executable** produces a compiled binary that can be executed. A 281 | **test suite** is a program that can be invoked like an executable but it 282 | generally just tests code that is internal to the package. A **benchmark** is 283 | used to measure the performance characteristics of another component. 284 | 285 | > Note: each of these components will receive individual treatment later on in 286 | > the guide. If you are interested to take a peek now you can find them here: 287 | > [library](../new_to_cabal/06_first_cabal_library.md), 288 | > [executable](../new_to_cabal/06_first_cabal_executable.md), 289 | > [test-suite](../leveling_up/02_first_cabal_test_suite.md) > 290 | > [benchmark](../getting_fancy/05_profiling_and_benchmarking.md) 291 | 292 | There are specific field names that we use to enumerate modules within 293 | components: `exposed-modules`, `other-modules`, `virtual-modules`, 294 | `test-module`, and even `main-is` which describes the entrypoint module for an 295 | executable. These fields are contained within the top level component 296 | declarations (mentioned above). 297 | 298 | External dependencies are also contained within a top level component 299 | declaration, under the `build-depends` label. There are also foreign 300 | (non-Haskell) dependencies, which live under the `foreign-library` field name. 301 | There are also `c-sources`, `extra-libraries`, `includes` and 302 | `install-includes`, but these are more advanced. These are a bit anomalous in 303 | the sense that they are not associated with a single component, there is a 304 | separate [chapter](../getting_fancy/06_foreign_libraries.md) on them. 305 | 306 | It is not necessary to commit these fields to memory, but it is good to be aware 307 | of them. Hopefully when you look at the layout of a `.cabal` file next, you will 308 | see the `package <- component <- module` hierarchy reflected in the nested 309 | fields! 310 | 311 | ## What is a package? 312 | 313 | If a module is the smallest unit of code in cabal, a **package** is the largest 314 | unit of distribution. At its core, a package is just a distributable artefact 315 | that provides access to components. 316 | 317 | > You might remember cabal projects from earlier, as being _upstream_ of 318 | > packages. Good catch, a **project** is bigger than a package in the sense that 319 | > it is a grouping of packages, but a project is not distributed; you will not 320 | > see a cabal project on Hackage. 321 | 322 | The `.cabal` file represents a single package, and is really just a collection 323 | of metadata about the package's constituent parts. It defines the package's 324 | components, its internal dependency structure (list of modules), and the 325 | package's external dependencies; usually other packages. 326 | 327 | To make things more concrete, here is a pseudo-Haskell type representing the 328 | package-component-module hierarchy: 329 | 330 | ```haskell 331 | data Module = 332 | Module 333 | { moduleName :: Text 334 | , moduleEntities :: [Entities] 335 | , moduleExports :: [Entities] 336 | , moduleModuleImports :: [Module] 337 | } 338 | 339 | data ComponentType = Executable | Library | TestSuite | Benchmark 340 | 341 | data Component = 342 | Component 343 | { componentName :: Text 344 | , componentType :: ComponentType 345 | , componentModules :: [Module] 346 | } 347 | 348 | type Package = [Component] 349 | ``` 350 | 351 | > Note: technically a package can contain no components, but if you try and run 352 | > `cabal build` you will get a message suggesting that the omission may be 353 | > erroneous. 354 | 355 | ## Summary 356 | 357 | Cabal is a tool for defining and building Haskell packages. Even if we don't 358 | intend to distribute our code, we can still think of it as an unrealized 359 | package. This is important because, inherent in the definition of a package, are 360 | all of its dependencies. This is a very common use case for cabal; to bring a 361 | bunch of dependencies in to scope so that we can explore them and call them from 362 | our own code. This workflow, in which the code is not intended to be published 363 | (or even saved), is facilitated by commands like `cabal repl`, `cabal exec`, and 364 | `cabal env` (which is being worked on currently). 365 | 366 | Therefore it is informative to think of cabal as handling several distinct 367 | concerns: 368 | 369 | 1. Describing the internal dependency structure of a package in terms of 370 | modules. 371 | 2. Managing the external dependencies of our project and allowing us to access 372 | them from within our modules. 373 | 3. Managing the building and distribution of our package, which is really just a 374 | collection of components. 375 | 376 | I believe that the reader of this guide will get the most out of it if they 377 | consider which of these functions they are interested in performing with Cabal. 378 | Hopefully understanding these distinct considerations will reveal some of the 379 | intention behind subsequent sections! 380 | -------------------------------------------------------------------------------- /src/preamble/03_what_is_hackage.md: -------------------------------------------------------------------------------- 1 | # What is hackage? 2 | -------------------------------------------------------------------------------- /src/preamble/04_how_to_install_software_with_cabal-install.md: -------------------------------------------------------------------------------- 1 | # How to install software with cabal-install 2 | --------------------------------------------------------------------------------