├── {{cookiecutter.project_name_slug}} ├── .envrc ├── .ghcid ├── src │ └── Lib.hs ├── test │ ├── Spec.hs │ └── {{cookiecutter.module}} │ │ └── ExampleSpec.hs ├── weeder.dhall ├── app │ └── Main.hs ├── .ghci ├── .dir-locals.el ├── default.nix ├── .gitignore ├── apply-refact.nix ├── hlint.nix ├── weeder.nix ├── cabal-fmt.nix ├── stylish-haskell.nix ├── haskell-pkgs.nix ├── LICENSE ├── shell.nix ├── README.md ├── {{cookiecutter.project_name_slug}}.cabal └── .stylish-haskell.yaml ├── hooks └── post_gen_project.py ├── cookiecutter.json └── README.md /{{cookiecutter.project_name_slug}}/.envrc: -------------------------------------------------------------------------------- 1 | use nix 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/.ghcid: -------------------------------------------------------------------------------- 1 | --command=cabal repl -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/src/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib where 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/test/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -F -pgmF hspec-discover #-} 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/weeder.dhall: -------------------------------------------------------------------------------- 1 | { roots = [ "^Main.main$" ], type-class-roots = True } -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | main :: IO () 4 | main = putStrLn "Hello World!" 5 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/.ghci: -------------------------------------------------------------------------------- 1 | :def pf \str -> return $ ":! pointfree \"" ++ str ++ "\"" 2 | :set prompt "\ESC[1;35m%s\n\ESC[0;34mλ> \ESC[m" 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;; this is a cabal project, thus we don't want to have stack flycheck enabled 2 | ((haskell-mode . ((flycheck-disabled-checkers . (haskell-stack-ghc))))) 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./haskell-pkgs.nix 2 | , haskellCompiler ? "ghc8102" 3 | }: 4 | pkgs.haskell-nix.cabalProject { 5 | src = pkgs.haskell-nix.haskellLib.cleanGit { 6 | name = "{{cookiecutter.project_name_slug}}"; 7 | src = ./.; 8 | }; 9 | compiler-nix-name = haskellCompiler; 10 | } 11 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | cabal-dev 4 | *.o 5 | *.hi 6 | *.hie 7 | *.chi 8 | *.chs.h 9 | *.dyn_o 10 | *.dyn_hi 11 | .hpc 12 | .hsenv 13 | .cabal-sandbox/ 14 | cabal.sandbox.config 15 | *.prof 16 | *.aux 17 | *.hp 18 | *.eventlog 19 | .stack-work/ 20 | cabal.project.local 21 | cabal.project.local~ 22 | .HTF/ 23 | .ghc.environment.* 24 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/apply-refact.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./haskell-pkgs.nix 2 | }: 3 | 4 | let 5 | hsPkgs = pkgs.haskell-nix.stackProject { 6 | compiler-nix-name = "ghc8102"; 7 | modules = []; 8 | src = pkgs.fetchFromGitHub { 9 | owner = "mpickering"; 10 | repo = "apply-refact"; 11 | rev = "311fe6cc3fb2ce472c0e9a1d1d9eeb7187ed2638"; 12 | sha256 = "0wyamn4w3lsayhsqs6h60f0m337b1p5byw1frmcyima3pqsgprvl"; 13 | }; 14 | }; 15 | in hsPkgs.apply-refact.components.exes.refactor 16 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/hlint.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./haskell-pkgs.nix 2 | }: 3 | 4 | let 5 | hsPkgs = pkgs.haskell-nix.stackProject { 6 | compiler-nix-name = "ghc8102"; 7 | modules = []; 8 | src = pkgs.fetchFromGitHub { 9 | owner = "ndmitchell"; 10 | repo = "hlint"; 11 | # 3.2.1 release 12 | rev = "57b664d77d27845629c093f6479b6104cf6455e2"; 13 | sha256 = "0y0c7vk9p6wj7vx4cfn6h0mg62jy03d0s54ydhmwv5c7112bcpqf"; 14 | }; 15 | }; 16 | in hsPkgs.hlint.components.exes.hlint 17 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/weeder.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./haskell-pkgs.nix 2 | }: 3 | 4 | let 5 | hsPkgs = pkgs.haskell-nix.cabalProject { 6 | compiler-nix-name = "ghc8102"; 7 | modules = []; 8 | src = pkgs.fetchFromGitHub { 9 | owner = "ocharles"; 10 | repo = "weeder"; 11 | # 2.1.3 release 12 | rev = "f31432b2adc8953cd5763f03a14ac3047a333b3f"; 13 | sha256 = "19w8j7y3g6x0400q22zrx19vnq11gj4mby2ahf54jkw275zkllw0"; 14 | }; 15 | }; 16 | in hsPkgs.weeder.components.exes.weeder 17 | -------------------------------------------------------------------------------- /hooks/post_gen_project.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | print(os.getcwd()) 5 | 6 | def remove(filepath): 7 | if os.path.isfile(filepath): 8 | os.remove(filepath) 9 | elif os.path.isdir(filepath): 10 | shutil.rmtree(filepath) 11 | 12 | create_direnv = '{{cookiecutter.create_direnv}}' == 'y' 13 | enable_emacs_integration = '{{cookiecutter.enable_emacs_integration}}' == 'y' 14 | 15 | if not create_direnv: 16 | remove('.envrc') 17 | 18 | if not enable_emacs_integration: 19 | remove('.dir-locals.el') 20 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/cabal-fmt.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./haskell-pkgs.nix 2 | }: 3 | 4 | let 5 | hsPkgs = pkgs.haskell-nix.cabalProject { 6 | compiler-nix-name = "ghc8102"; 7 | modules = []; 8 | src = pkgs.fetchFromGitHub { 9 | owner = "phadej"; 10 | repo = "cabal-fmt"; 11 | # 0.1.5 release 12 | rev = "dc1e63bb10ddafd6152fae8f7206c3b201b0f7ab"; 13 | sha256 = "14mbi96m4ci2pjxr8y998mjghbwpwz2c45wp1kn4j5b7pl7bqv21"; 14 | }; 15 | }; 16 | in hsPkgs.cabal-fmt.components.exes.cabal-fmt 17 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/stylish-haskell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./haskell-pkgs.nix 2 | }: 3 | 4 | let 5 | hsPkgs = pkgs.haskell-nix.stackProject { 6 | compiler-nix-name = "ghc8102"; 7 | modules = []; 8 | src = pkgs.fetchFromGitHub { 9 | owner = "jaspervdj"; 10 | repo = "stylish-haskell"; 11 | # 0.12.2.0 release 12 | rev = "84770e33bb6286c163c3b2b10fa98d264f6672b8"; 13 | sha256 = "1jc844x8v93xgnl6fjrk31gb2zr96nxbqmgmnc4hdfhfyajh5y7w"; 14 | }; 15 | }; 16 | in hsPkgs.stylish-haskell.components.exes.stylish-haskell 17 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "Enter project name", 3 | "project_name_slug": "{{ cookiecutter.project_name|lower|replace(' ', '-')|replace('_', '-') }}", 4 | "project_synopsis": "Enter project synopsis", 5 | "module": "{{cookiecutter.project_name_slug|title|replace('-', '')}}", 6 | "category": "Unclassified", 7 | "author_name": "Your Name", 8 | "gh_user": "your_github_username", 9 | "formatter_indent" : "2", 10 | "formatter_columns" : "110", 11 | "enable_emacs_integration" : "y", 12 | "create_direnv" : "{{cookiecutter.enable_emacs_integration}}" 13 | } 14 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/test/{{cookiecutter.module}}/ExampleSpec.hs: -------------------------------------------------------------------------------- 1 | module {{cookiecutter.module}}.ExampleSpec where 2 | 3 | import qualified Hedgehog.Gen as Gen 4 | import qualified Hedgehog.Range as Range 5 | import Test.Hspec (before, describe, hspec, it, shouldBe, Spec) 6 | import Test.Hspec.Hedgehog (PropertyT, diff, forAll, hedgehog, (/==), (===)) 7 | 8 | spec :: Spec 9 | spec = do 10 | describe "unit tests" $ do 11 | it "should work" $ do 12 | True `shouldBe` True 13 | describe "hedgehog property tests" $ do 14 | it "should work" $ hedgehog $ do 15 | x <- forAll $ Gen.integral (Range.linear 0 1000) 16 | y <- forAll $ Gen.integral (Range.linear 0 5000) 17 | diff (x + y) (>=) x 18 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/haskell-pkgs.nix: -------------------------------------------------------------------------------- 1 | let 2 | # Fetch the latest haskell.nix and import its default.nix 3 | haskellNix = import (builtins.fetchTarball{ 4 | url = "https://github.com/input-output-hk/haskell.nix/archive/f6663a8449f5e4a7393aa24601600c8f6e352c97.tar.gz"; 5 | }) {}; 6 | 7 | # haskell.nix provides access to the nixpkgs pins which are used by our CI, 8 | # hence you will be more likely to get cache hits when using these. 9 | # But you can also just use your own, e.g. ''. 10 | nixpkgsSrc = haskellNix.sources.nixpkgs-2003; 11 | 12 | # haskell.nix provides some arguments to be passed to nixpkgs, including some 13 | # patches and also the haskell.nix functionality itself as an overlay. 14 | nixpkgsArgs = haskellNix.nixpkgsArgs; 15 | 16 | # import nixpkgs with overlays 17 | in import nixpkgsSrc nixpkgsArgs 18 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 {{cookiecutter.author_name}} 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/shell.nix: -------------------------------------------------------------------------------- 1 | # shell.nix 2 | { pkgs ? import ./haskell-pkgs.nix 3 | }: 4 | 5 | let 6 | hsPkgs = import ./. { inherit pkgs; }; 7 | hlint = import ./hlint.nix { inherit pkgs; }; 8 | apply-refact = import ./apply-refact.nix { inherit pkgs; }; 9 | stylish-haskell = import ./stylish-haskell.nix { inherit pkgs; }; 10 | cabal-fmt = import ./cabal-fmt.nix { inherit pkgs; }; 11 | weeder = import ./weeder.nix { inherit pkgs; }; 12 | in 13 | hsPkgs.shellFor { 14 | tools = { cabal = "3.2.0.0"; }; 15 | withHoogle = true; 16 | shellHook = '' 17 | format-all () { 18 | git rev-parse --show-toplevel | xargs -i stylish-haskell -ric "{}/.stylish-haskell.yaml" $@ 19 | cabal-fmt --inplace {{cookiecutter.project_name_slug}}.cabal 20 | } 21 | 22 | hlint-all () { 23 | hlint . 24 | } 25 | 26 | hlint-all-fix () { 27 | find . -path ./dist-newstyle -prune -o -name \*.hs -exec hlint --refactor --refactor-options="--inplace" {} \; 28 | } 29 | 30 | ''; 31 | 32 | buildInputs = 33 | [ pkgs.haskellPackages.ghcid 34 | pkgs.haskellPackages.hp2pretty 35 | pkgs.haskellPackages.pointfree 36 | cabal-fmt 37 | hlint 38 | apply-refact 39 | stylish-haskell 40 | weeder 41 | ]; 42 | } 43 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/README.md: -------------------------------------------------------------------------------- 1 | # {{cookiecutter.project_name}} 2 | 3 | ## Developement 4 | 5 | ### Pre-requirements 6 | 7 | This project is build using [haskell.nix](https://input-output-hk.github.io/haskell.nix/). This means that the only requirement for building this project is having [Nix installed](https://nixos.org/download.html) on your system. 8 | 9 | In order to *download* GHC compiler from pre-build cache, please add the following sections to `/etc/nix/nix.conf` or `~/.config/nix/nix.conf`. 10 | 11 | ``` 12 | trusted-public-keys = [...] hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= [...] 13 | substituters = [...] https://hydra.iohk.io [...] 14 | ``` 15 | 16 | Otherwise Nix will have to build several copies of GHC, before building this project - and this will take a while. 17 | 18 | ### Building the project 19 | 20 | Enter the nix-shell 21 | 22 | ``` 23 | $> nix-shell 24 | trace: Using latest index state for {{cookiecutter.project_name_slug}}! 25 | trace: Using index-state: 2020-09-14T00:00:00Z for haskell-template-project 26 | trace: Shell for {{cookiecutter.project_name_slug}} 27 | trace: Shell for {{cookiecutter.project_name_slug}} 28 | trace: Using index-state: 2020-05-31T00:00:00Z for hoogle 29 | (...) 30 | trace: Using latest index state for cabal-install! 31 | trace: Using index-state: 2020-09-14T00:00:00Z for cabal-install 32 | ``` 33 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/{{cookiecutter.project_name_slug}}.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 2.1 2 | 3 | name: {{cookiecutter.project_name_slug}} 4 | version: 0.1.0.0 5 | license-file: LICENSE 6 | author: {{cookiecutter.author_name}} 7 | maintainer: {{cookiecutter.author_name}} 8 | copyright: {{cookiecutter.author_name}} 9 | category: {{cookiecutter.category}} 10 | build-type: Simple 11 | 12 | extra-source-files: 13 | README.md 14 | 15 | source-repository head 16 | type: git 17 | location: https://github.com/{{cookiecutter.gh_user}}/{{cookiecutter.project_name_slug}} 18 | 19 | library 20 | exposed-modules: 21 | Lib 22 | hs-source-dirs: 23 | src 24 | build-depends: 25 | base >=4.7 && <5 26 | , text 27 | default-language: Haskell2010 28 | 29 | executable {{cookiecutter.project_name_slug}}-exe 30 | main-is: Main.hs 31 | hs-source-dirs: 32 | app 33 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 34 | build-depends: 35 | base >=4.7 && <5 36 | , {{cookiecutter.project_name_slug}} 37 | default-language: Haskell2010 38 | 39 | test-suite {{cookiecutter.project_name_slug}}-test 40 | type: exitcode-stdio-1.0 41 | default-language: Haskell2010 42 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 43 | hs-source-dirs: test 44 | main-is: Spec.hs 45 | other-modules: 46 | {{cookiecutter.module}}.ExampleSpec 47 | -- dependencies in library 48 | build-depends: 49 | , base 50 | , hspec 51 | , hedgehog 52 | , hspec-hedgehog 53 | , {{cookiecutter.project_name_slug}} 54 | build-tool-depends: hspec-discover:hspec-discover == 2.* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Haskell Starter-Kit 2 | 3 | This project is a dynamic template for a modern Haskell development environment. With a single command it bootstraps a *haskell.nix* project, shell with rich ecosystem (hlint, ghcid), out of the box support for profiling, Emacs integration and many more. 4 | 5 | ## Key features 6 | 7 | * Latests building environment 8 | * GHC (8.10.2) 9 | * Cabal (3.2.0.0) 10 | * ghcid 11 | * Linter support 12 | * hlint (3.2.1) 13 | * apply-refact 14 | * pointfree 15 | * weeder (2.1.3) 16 | * Formatters 17 | * stylish-haskell (0.12.2.0) 18 | * cabal-fmt 19 | * Testing environment for both unit and property-based tests 20 | * hspec 21 | * hedgehog 22 | * Other 23 | * local Hoogle server 24 | * direnv 25 | * sleek Emacs integration 26 | 27 | ## How to create new project using this template 28 | 29 | *pre-requirement*: you have [Nix installed](https://nixos.org/download.html) on your system 30 | 31 | ``` 32 | nix-shell -p cookiecutter git --run 'cookiecutter gh:EncodePanda/haskell-starter-kit 33 | ``` 34 | 35 | ### Set defaults 36 | 37 | Reentering the defaults (like github user name) might be tedious and boring. That's why it is a good practice to setup the defaults. 38 | Modify `~/.cookiecutterrc` by providing default value for github user and author name. 39 | In my case this is: 40 | 41 | ``` 42 | default_context: 43 | "gh_user": "EncodePanda" 44 | "author_name": "Paweł Szulc" 45 | ``` 46 | 47 | ## Example 48 | 49 | ### 1. Create the project using template 50 | 51 | ``` 52 | ➜ ~ nix-shell -p cookiecutter git --run 'cookiecutter gh:EncodePanda/haskell-template-project' 53 | You've downloaded /Users/rabbit/.cookiecutters/haskell-template-project before. Is it okay to delete and re-download it? [yes]: 54 | project_name [Enter project name]: foo 55 | project_synopsis [Enter project synopsis]: Example project 56 | module [Foo]: 57 | category [Unclassified]: Example 58 | author_name [Paweł Szulc]: 59 | gh_user [EncodePanda]: 60 | formatter_indent [2]: 61 | formatter_columns [110]: 80 62 | ``` 63 | 64 | ### 2. Enter nix-shell 65 | 66 | *note*: Before entering the nix-shell consider reading README.md, there is a section about using Nix cache - so that you don't have to recompile the whole world 67 | 68 | ``` 69 | ➜ ~ cd foo 70 | ➜ foo nix-shell 71 | trace: gitSource.nix: /Users/rabbit/foo does not seem to be a git repository, 72 | assuming it is a clean checkout. 73 | trace: Using latest index state for foo! 74 | (...) 75 | trace: Using latest index state for cabal-install! 76 | trace: Using index-state: 2020-09-14T00:00:00Z for cabal-install 77 | ``` 78 | 79 | ### 3. Build the project 80 | 81 | ``` 82 | [nix-shell:~/foo]$ cabal build 83 | Resolving dependencies... 84 | Build profile: -w ghc-8.10.2 -O1 85 | (...) 86 | Building library for foo-0.1.0.0.. 87 | [1 of 1] Compiling Lib ( src/Lib.hs, /Users/rabbit/foo/dist-newstyle/build/x86_64-osx/ghc-8.10.2/foo-0.1.0.0/build/Lib.o, /Users/rabbit/foo/dist-newstyle/build/x86_64-osx/ghc-8.10.2/foo-0.1.0.0/build/Lib.dyn_o ) 88 | (...) 89 | [1 of 1] Compiling Main ( app/Main.hs, /Users/rabbit/foo/dist-newstyle/build/x86_64-osx/ghc-8.10.2/foo-0.1.0.0/x/foo-exe/build/foo-exe/foo-exe-tmp/Main.o ) 90 | Linking /Users/rabbit/foo/dist-newstyle/build/x86_64-osx/ghc-8.10.2/foo-0.1.0.0/x/foo-exe/build/foo-exe/foo-exe ... 91 | ``` 92 | 93 | ### 4. Run tests 94 | 95 | ``` 96 | [nix-shell:~/foo]$ cabal test --test-show-details=streaming 97 | Build profile: -w ghc-8.10.2 -O1 98 | (...) 99 | Running 1 test suites... 100 | Test suite foo-test: RUNNING... 101 | 102 | Foo.Example 103 | unit tests 104 | should work 105 | hedgehog property tests 106 | should work 107 | 108 | Finished in 0.0022 seconds 109 | 2 examples, 0 failures 110 | Test suite foo-test: PASS 111 | Test suite logged to: 112 | 1 of 1 test suites (1 of 1 test cases) passed. 113 | 114 | ``` 115 | 116 | ### 5. Use linter 117 | 118 | look for issues, non found 119 | 120 | ``` 121 | [nix-shell:~/foo]$ hlint-all 122 | No hints 123 | ``` 124 | 125 | introduce code that is not perfect 126 | 127 | ``` 128 | [nix-shell:~/foo]$ echo "foo :: String -> Int" >> src/Lib.hs; echo "foo = \_ -> 10" >> src/Lib.hs 129 | 130 | [nix-shell:~/foo]$ cat src/Lib.hs 131 | module Lib where 132 | foo :: String -> Int 133 | foo = \_ -> 10 134 | ``` 135 | 136 | look for issues one more time, found it! 137 | 138 | ``` 139 | [nix-shell:~/foo]$ hlint-all 140 | src/Lib.hs:3:1-14: Warning: Redundant lambda 141 | Found: 142 | foo = \ _ -> 10 143 | Perhaps: 144 | foo _ = 10 145 | 146 | src/Lib.hs:3:7-14: Suggestion: Use const 147 | Found: 148 | \ _ -> 10 149 | Perhaps: 150 | const 10 151 | 152 | 2 hints 153 | ``` 154 | 155 | ask linter to automatically fix them 156 | 157 | ``` 158 | [nix-shell:~/foo]$ hlint-all-fix 159 | 160 | [nix-shell:~/foo]$ cat src/Lib.hs 161 | module Lib where 162 | foo :: String -> Int 163 | foo _ = 10 164 | 165 | [nix-shell:~/foo]$ hlint-all 166 | No hints 167 | ``` 168 | 169 | ### 6. Use ghcid 170 | 171 | ``` 172 | [nix-shell:~/projects/Foo]$ ghcid 173 | Loading cabal repl ... 174 | Build profile: -w ghc-8.10.1 -O1 175 | In order, the following will be built (use -v for more details): 176 | - Foo-0.1.0.0 (lib) (ephemeral targets) 177 | Preprocessing library for Foo-0.1.0.0.. 178 | GHCi, version 8.10.1: https://www.haskell.org/ghc/ :? for help 179 | [1 of 1] Compiling Lib ( src/Lib.hs, interpreted ) 180 | Ok, one module loaded. 181 | 182 | All good (1 module, at 23:56:46) 183 | 184 | 185 | 186 | 187 | ``` 188 | 189 | ### 7. Hoogle support 190 | 191 | ``` 192 | [nix-shell:~/projects/foo]$ hoogle server --local 193 | Server started on port 8080 194 | Reading log...0.00s 195 | 2021-01-05T15:39:39.488174 - Server starting on port 8080 and host/IP Host "127.0.0.1" 196 | ``` 197 | 198 | ### 8. Pointfree support 199 | 200 | 201 | You can use pointfree in the shell 202 | ``` 203 | [nix-shell:~/projects/foo]$ pointfree "foo list = fmap (+1) list" 204 | foo = fmap (1 +) 205 | ``` 206 | 207 | or you can use it in the REPL 208 | 209 | ``` 210 | [nix-shell:~/projects/foo]$ cabal repl 211 | Loaded GHCi configuration from /Users/rabbit/projects/foo/.ghci 212 | [1 of 1] Compiling Lib ( src/Lib.hs, interpreted ) 213 | Ok, one module loaded. 214 | 215 | λ> :pf foo list = fmap (+1) list 216 | foo = fmap (1 +) 217 | *Lib 218 | ``` 219 | 220 | ### 9. Weeder support 221 | 222 | ``` 223 | [nix-shell:~/projects/foo]$ weeder 224 | Weeds detected: 0 225 | 226 | [nix-shell:~/projects/foo]$ echo "notUsed :: Int" >> src/Lib.hs 227 | [nix-shell:~/projects/foo]$ echo "notUsed = 10" >> src/Lib.hs 228 | 229 | [nix-shell:~/projects/foo]$ cat src/Lib.hs 230 | module Lib where 231 | notUsed :: Int 232 | notUsed = 10 233 | 234 | [nix-shell:~/projects/foo]$ cabal build 235 | (...) 236 | Preprocessing executable 'foo-exe' for foo-0.1.0.0.. 237 | Building executable 'foo-exe' for foo-0.1.0.0.. 238 | (...) 239 | Linking ~/projects/foo/dist-newstyle/build/x86_64-osx/ghc-8.10.2/ 240 | 241 | [nix-shell:~/projects/foo]$ weeder 242 | src/Lib.hs:3:1: error: notUsed is unused 243 | 244 | 1 ┃ module Lib where 245 | 2 ┃ notUsed :: Int 246 | 3 ┃ notUsed = 10 247 | 248 | Delete this definition or add ‘Lib.notUsed’ as a root to fix this error. 249 | 250 | Weeds detected: 1 251 | ``` 252 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name_slug}}/.stylish-haskell.yaml: -------------------------------------------------------------------------------- 1 | # stylish-haskell configuration file 2 | # ================================== 3 | 4 | # The stylish-haskell tool is mainly configured by specifying steps. These steps 5 | # are a list, so they have an order, and one specific step may appear more than 6 | # once (if needed). Each file is processed by these steps in the given order. 7 | steps: 8 | # Convert some ASCII sequences to their Unicode equivalents. This is disabled 9 | # by default. 10 | # - unicode_syntax: 11 | # # In order to make this work, we also need to insert the UnicodeSyntax 12 | # # language pragma. If this flag is set to true, we insert it when it's 13 | # # not already present. You may want to disable it if you configure 14 | # # language extensions using some other method than pragmas. Default: 15 | # # true. 16 | # add_language_pragma: true 17 | 18 | # Format module header 19 | # 20 | # Currently, this option is not configurable and will format all exports and 21 | # module declarations to minimize diffs 22 | # 23 | - module_header: 24 | # # How many spaces use for indentation in the module header. 25 | indent: {{cookiecutter.formatter_indent}} 26 | # 27 | # # Should export lists be sorted? Sorting is only performed within the 28 | # # export section, as delineated by Haddock comments. 29 | sort: true 30 | # 31 | # # See `separate_lists` for the `imports` step. 32 | separate_lists: true 33 | 34 | # Format record definitions. This is disabled by default. 35 | # 36 | # You can control the layout of record fields. The only rules that can't be configured 37 | # are these: 38 | # 39 | # - "|" is always aligned with "=" 40 | # - "," in fields is always aligned with "{" 41 | # - "}" is likewise always aligned with "{" 42 | # 43 | # - records: 44 | # # How to format equals sign between type constructor and data constructor. 45 | # # Possible values: 46 | # # - "same_line" -- leave "=" AND data constructor on the same line as the type constructor. 47 | # # - "indent N" -- insert a new line and N spaces from the beginning of the next line. 48 | # equals: "indent 2" 49 | # 50 | # # How to format first field of each record constructor. 51 | # # Possible values: 52 | # # - "same_line" -- "{" and first field goes on the same line as the data constructor. 53 | # # - "indent N" -- insert a new line and N spaces from the beginning of the data constructor 54 | # first_field: "indent 2" 55 | # 56 | # # How many spaces to insert between the column with "," and the beginning of the comment in the next line. 57 | # field_comment: 2 58 | # 59 | # # How many spaces to insert before "deriving" clause. Deriving clauses are always on separate lines. 60 | # deriving: 2 61 | # 62 | # # How many spaces to insert before "via" clause counted from indentation of deriving clause 63 | # # Possible values: 64 | # # - "same_line" -- "via" part goes on the same line as "deriving" keyword. 65 | # # - "indent N" -- insert a new line and N spaces from the beginning of "deriving" keyword. 66 | # via: "indent 2" 67 | # 68 | # # Sort typeclass names in the "deriving" list alphabetically. 69 | # sort_deriving: true 70 | # 71 | # # Wheter or not to break enums onto several lines 72 | # # 73 | # # Default: false 74 | # break_enums: false 75 | # 76 | # # Whether or not to break single constructor data types before `=` sign 77 | # # 78 | # # Default: true 79 | # break_single_constructors: true 80 | # 81 | # # Whether or not to curry constraints on function. 82 | # # 83 | # # E.g: @allValues :: Enum a => Bounded a => Proxy a -> [a]@ 84 | # # 85 | # # Instead of @allValues :: (Enum a, Bounded a) => Proxy a -> [a]@ 86 | # # 87 | # # Default: false 88 | # curried_context: false 89 | 90 | # Align the right hand side of some elements. This is quite conservative 91 | # and only applies to statements where each element occupies a single 92 | # line. 93 | # Possible values: 94 | # - always - Always align statements. 95 | # - adjacent - Align statements that are on adjacent lines in groups. 96 | # - never - Never align statements. 97 | # All default to always. 98 | - simple_align: 99 | cases: always 100 | top_level_patterns: always 101 | records: always 102 | multi_way_if: always 103 | 104 | # Import cleanup 105 | - imports: 106 | # There are different ways we can align names and lists. 107 | # 108 | # - global: Align the import names and import list throughout the entire 109 | # file. 110 | # 111 | # - file: Like global, but don't add padding when there are no qualified 112 | # imports in the file. 113 | # 114 | # - group: Only align the imports per group (a group is formed by adjacent 115 | # import lines). 116 | # 117 | # - none: Do not perform any alignment. 118 | # 119 | # Default: global. 120 | align: global 121 | 122 | # The following options affect only import list alignment. 123 | # 124 | # List align has following options: 125 | # 126 | # - after_alias: Import list is aligned with end of import including 127 | # 'as' and 'hiding' keywords. 128 | # 129 | # > import qualified Data.List as List (concat, foldl, foldr, head, 130 | # > init, last, length) 131 | # 132 | # - with_alias: Import list is aligned with start of alias or hiding. 133 | # 134 | # > import qualified Data.List as List (concat, foldl, foldr, head, 135 | # > init, last, length) 136 | # 137 | # - with_module_name: Import list is aligned `list_padding` spaces after 138 | # the module name. 139 | # 140 | # > import qualified Data.List as List (concat, foldl, foldr, head, 141 | # init, last, length) 142 | # 143 | # This is mainly intended for use with `pad_module_names: false`. 144 | # 145 | # > import qualified Data.List as List (concat, foldl, foldr, head, 146 | # init, last, length, scanl, scanr, take, drop, 147 | # sort, nub) 148 | # 149 | # - new_line: Import list starts always on new line. 150 | # 151 | # > import qualified Data.List as List 152 | # > (concat, foldl, foldr, head, init, last, length) 153 | # 154 | # - repeat: Repeat the module name to align the import list. 155 | # 156 | # > import qualified Data.List as List (concat, foldl, foldr, head) 157 | # > import qualified Data.List as List (init, last, length) 158 | # 159 | # Default: after_alias 160 | list_align: after_alias 161 | 162 | # Right-pad the module names to align imports in a group: 163 | # 164 | # - true: a little more readable 165 | # 166 | # > import qualified Data.List as List (concat, foldl, foldr, 167 | # > init, last, length) 168 | # > import qualified Data.List.Extra as List (concat, foldl, foldr, 169 | # > init, last, length) 170 | # 171 | # - false: diff-safe 172 | # 173 | # > import qualified Data.List as List (concat, foldl, foldr, init, 174 | # > last, length) 175 | # > import qualified Data.List.Extra as List (concat, foldl, foldr, 176 | # > init, last, length) 177 | # 178 | # Default: true 179 | pad_module_names: true 180 | 181 | # Long list align style takes effect when import is too long. This is 182 | # determined by 'columns' setting. 183 | # 184 | # - inline: This option will put as much specs on same line as possible. 185 | # 186 | # - new_line: Import list will start on new line. 187 | # 188 | # - new_line_multiline: Import list will start on new line when it's 189 | # short enough to fit to single line. Otherwise it'll be multiline. 190 | # 191 | # - multiline: One line per import list entry. 192 | # Type with constructor list acts like single import. 193 | # 194 | # > import qualified Data.Map as M 195 | # > ( empty 196 | # > , singleton 197 | # > , ... 198 | # > , delete 199 | # > ) 200 | # 201 | # Default: inline 202 | long_list_align: inline 203 | 204 | # Align empty list (importing instances) 205 | # 206 | # Empty list align has following options 207 | # 208 | # - inherit: inherit list_align setting 209 | # 210 | # - right_after: () is right after the module name: 211 | # 212 | # > import Vector.Instances () 213 | # 214 | # Default: inherit 215 | empty_list_align: inherit 216 | 217 | # List padding determines indentation of import list on lines after import. 218 | # This option affects 'long_list_align'. 219 | # 220 | # - : constant value 221 | # 222 | # - module_name: align under start of module name. 223 | # Useful for 'file' and 'group' align settings. 224 | # 225 | # Default: 4 226 | list_padding: {{cookiecutter.formatter_indent}} 227 | 228 | # Separate lists option affects formatting of import list for type 229 | # or class. The only difference is single space between type and list 230 | # of constructors, selectors and class functions. 231 | # 232 | # - true: There is single space between Foldable type and list of it's 233 | # functions. 234 | # 235 | # > import Data.Foldable (Foldable (fold, foldl, foldMap)) 236 | # 237 | # - false: There is no space between Foldable type and list of it's 238 | # functions. 239 | # 240 | # > import Data.Foldable (Foldable(fold, foldl, foldMap)) 241 | # 242 | # Default: true 243 | separate_lists: true 244 | 245 | # Space surround option affects formatting of import lists on a single 246 | # line. The only difference is single space after the initial 247 | # parenthesis and a single space before the terminal parenthesis. 248 | # 249 | # - true: There is single space associated with the enclosing 250 | # parenthesis. 251 | # 252 | # > import Data.Foo ( foo ) 253 | # 254 | # - false: There is no space associated with the enclosing parenthesis 255 | # 256 | # > import Data.Foo (foo) 257 | # 258 | # Default: false 259 | space_surround: false 260 | 261 | # Enabling this argument will use the new GHC lib parse to format imports. 262 | # 263 | # This currently assumes a few things, it will assume that you want post 264 | # qualified imports. It is also not as feature complete as the old 265 | # imports formatting. 266 | # 267 | # It does not remove redundant lines or merge lines. As such, the full 268 | # feature scope is still pending. 269 | # 270 | # It _is_ however, a fine alternative if you are using features that are 271 | # not parseable by haskell src extensions and you're comfortable with the 272 | # presets. 273 | # 274 | # Default: false 275 | ghc_lib_parser: false 276 | 277 | # Language pragmas 278 | - language_pragmas: 279 | # We can generate different styles of language pragma lists. 280 | # 281 | # - vertical: Vertical-spaced language pragmas, one per line. 282 | # 283 | # - compact: A more compact style. 284 | # 285 | # - compact_line: Similar to compact, but wrap each line with 286 | # `{-#LANGUAGE #-}'. 287 | # 288 | # Default: vertical. 289 | style: vertical 290 | 291 | # Align affects alignment of closing pragma brackets. 292 | # 293 | # - true: Brackets are aligned in same column. 294 | # 295 | # - false: Brackets are not aligned together. There is only one space 296 | # between actual import and closing bracket. 297 | # 298 | # Default: true 299 | align: true 300 | 301 | # stylish-haskell can detect redundancy of some language pragmas. If this 302 | # is set to true, it will remove those redundant pragmas. Default: true. 303 | remove_redundant: true 304 | 305 | # Language prefix to be used for pragma declaration, this allows you to 306 | # use other options non case-sensitive like "language" or "Language". 307 | # If a non correct String is provided, it will default to: LANGUAGE. 308 | language_prefix: LANGUAGE 309 | 310 | # Replace tabs by spaces. This is disabled by default. 311 | # - tabs: 312 | # # Number of spaces to use for each tab. Default: 8, as specified by the 313 | # # Haskell report. 314 | # spaces: 8 315 | 316 | # Remove trailing whitespace 317 | - trailing_whitespace: {} 318 | 319 | # Squash multiple spaces between the left and right hand sides of some 320 | # elements into single spaces. Basically, this undoes the effect of 321 | # simple_align but is a bit less conservative. 322 | # - squash: {} 323 | 324 | # A common setting is the number of columns (parts of) code will be wrapped 325 | # to. Different steps take this into account. 326 | # 327 | # Set this to null to disable all line wrapping. 328 | # 329 | # Default: 80. 330 | columns: {{cookiecutter.formatter_columns}} 331 | 332 | # By default, line endings are converted according to the OS. You can override 333 | # preferred format here. 334 | # 335 | # - native: Native newline format. CRLF on Windows, LF on other OSes. 336 | # 337 | # - lf: Convert to LF ("\n"). 338 | # 339 | # - crlf: Convert to CRLF ("\r\n"). 340 | # 341 | # Default: native. 342 | newline: native 343 | 344 | # Sometimes, language extensions are specified in a cabal file or from the 345 | # command line instead of using language pragmas in the file. stylish-haskell 346 | # needs to be aware of these, so it can parse the file correctly. 347 | # 348 | # No language extensions are enabled by default. 349 | # language_extensions: 350 | # - TemplateHaskell 351 | # - QuasiQuotes 352 | 353 | # Attempt to find the cabal file in ancestors of the current directory, and 354 | # parse options (currently only language extensions) from that. 355 | # 356 | # Default: true 357 | cabal: true 358 | --------------------------------------------------------------------------------