DarkMatter in Cyberspace
  • Home
  • Categories
  • Tags
  • Archives

Stack Notes


Updated in 2018.9.

Install and List Global Packages

Run stack install <pkg-name> outside a stack project folder to install a global package. Like npm install -g <pkg-name> or pip install <pkg-name> for a global pip.

For example, install Idris with stack install idris. If it's blocked by GFW, run export https_proxy=... http_proxy=... before stack install.

You can find the installed binary in folder ~/.local/bin.

List all installed global packages with stack exec ghc-pkg -- list outside a stack project folder.

Project Management

Create a new project from scratch with: stack new GetProgrammingWithHaskell new-template -p "author-email:leetschau@gmail" -p "author-name:Li Chao" -p "category:Study" -p "github-username:leetschau"

You can choose a resolver (other than the default one chosen by stack, see its explanation in the next section) with stack new --resolver lts-12.17 my-project .... List available reslovers with stack ls snapshots.

Project Structure

Now you get a folder named my-project. There are 3 config files in the project folder:

  • stack.yaml: the definition of your project, used by stack;

  • package.yaml: the definition of the package you are working on, in the format of hpack. hpack is a "modern alternative to the Cabal package format and follows different design principles";

  • my-project.cabal: generated by hpack based on package.yaml, This file is used by stack, so should be added in git ignore file;

Concepts

According to stack.yaml vs cabal package file:

A project has:

  • A resolver, which tells it about a snapshot (more on this later)

  • Extra dependencies on top of the snapshot

  • 0 or more local Cabal packages

  • Flag and GHC options configurations

  • And a bunch more Stack configuration

A package has:

  • A name and version

  • 0 or 1 libraries

  • 0 or more executables

  • A cabal file (or, as mentioned above, an hpack package.yaml that generates a cabal file)

  • And a bunch more

A project has 1 or more packages. The packages belonging to the project is listed in the packages: section in the stack.yaml. The default value is ., which means the project root folder.

Workflow

Create a new stack project based on an existing .cabal file: stack init.

Print the compiler version for an existing project: stack exec -- ghc --version or stack ghc -- --version. Here stack ghc is a shortcut for stack exec ghc.

The -- after exec tells Stack to pass the rest of the command line arguments to the specified program instead of parsing them itself.

You can also find the versions of resolver and compiler in the output of stack path.

Compile a Haskell script and create an executable: stack ghc -- Main.hs. Run a Haskell script: stack runghc -- Main.hs. If your script has no arguments, the -- in above command can be omitted.

Run a script with the compiler of specific version, and external packages: stack --resolver lts-6.15 --install-ghc runghc --package http-conduit http.hs. Note that we also included --install-ghc to make sure that the correct GHC is downloaded and installed if necessary.

Run Haskell script as a shell script:

$ cat << EOF > hello.hs
#!/usr/bin/env stack
-- stack --resolver lts-12.5 script
main = do
  print "What is your name?"
  name <- getLine
  print ("Hello " ++ name ++ "!")
EOF

$ stack hello.hs
$ chmod 755 hello.hs
$ ./hello.hs

Start a REPL in current project root: stack ghci.

Start a REPL in current project root with external package loaded: stack ghci --package http-client. The external library (here is http-client) files will be downloaded and compiled in both $PROJ_HOME/.stack-work/install and ~/.stack/snapshots folders.

Compile the whole project and create executable file with stack build. Run the executable with stack exec <exe-name>, for example: stack exec Code0103.

Documentation Viewer

View the documentation of a function, say "nub", with hoogle -i nub. Install it with apt install hoogle.

Or you can use hoogle with stack hoogle -- -i nub. Here -- means all parameters followed are the parameters of hoogle instead of stack. This command is a shorthand of stack hoogle -- search --info nub. See the full Documentation of hoogle with stack hoogle -- --help, and doc for search subcommand with stack hoogle -- search --help.

There's a problem: which version's documentation of nub you are querying? In the cabal scenario, you have only a fixed version of ghc and cabal. So this isn't a problem. But for stack, it's reasonable to worry about it.

When you run stack hoogle inside a stack project, the version is specified by the resolver directive in the file stack.yaml.

When you run stack hoogle outside a stack project, stack treat it as in a global-project whose configuration files in folder ~/.stack/global-project. So you'd better modify resolver directive of the file ~/.stack/global-project/stack.yaml to a mainstream version (for now, it is "lts-12.17"), then initialize this project with running command stack init (or stack init --force if the former failed) in ~/.stack/global-project (or any folder outside a stack project).

For convenience, add hg='stack hoogle -- -i' into ~/.bash_aliases. Now you can run hg <function-name> anywhere happily.

Quick Doc in vim

Add autocmd FileType haskell nnoremap <C-q> :execute ':!stack hoogle -- -i <cword>'<cr> into $MYVIMRC.

Here <cword> means word at cursor, see :help cword for more forms.

You can also define quick doc via abbreviations with autocmd FileType haskell cabbrev qd execute ':!stack hoogle -- -i <cword>', but this has more keystrokes than the map command above. And you have to press manually after :qd, instead of adding it into the shortcut (in this case you can't see the output of the command).

Haskell package management

Stack has different resolver according to different version of GHC. You can see all available resolvers at Stackage. For example, if you want to use compiler GHC 8.2.2, the corresponding resolver is LTS Haskell 11.22 (ghc-8.2.2). In the webpage of this resolver you can find a package base-4.10.1.0, which means the base package in this resolver has the version 4.10.1.0.

Each resolver contains packages with specific versions compatible with each other. You can read its package list at above mentioned Stackage, or in file ~/.stack/build-plan/lts-.yaml. The resolver version is defined in stack.yaml file in a project's root folder. So the package manager need not resolve package version when building. Stack saves each compiler (GHC executable) and libraries in folder ~/.stack/programs.

The package.yaml (or my-package.cabal) file defines which packages are used as dependencies by your package. While stack.yaml file specifies precisely which version for each dependency to be used during the building process with the versions of the resolver and extra-deps (packages outside the resolver).

The stack.yaml file serves a purpose similar to npm-shrinkwrap.json file, while project-name.cabal serves like the packages.json for a node.js project.

In some ways the style of stack is like pipenv: The environment is defined at project level (each project has a stack.yaml and a .cabal file), in contrast with the conda style (the environment is defined at system level) The actual environment files are saved in a system-level folder ~/.stack, in contrast with the node.js style: save all files in node_modules folder inside the project.

stack install always install packages at the system-level, so there's no distinction between npm install and npm install -g. Multiple Haskell projects can share one resolver, like conda, while is a better stratagy than pipenv and npm (libraries can't be shared between projects).

Ref:

  • stack.yaml file & .cabal file differences?

  • How to Build with Stack

  • What is the difference between Cabal and Stack?

  • How to Build with Stack

  • Build your Haskell project continuously

  • YAML Configuration



Published

Sep 23, 2015

Last Updated

Sep 6, 2019

Category

Tech

Tags

  • haskell 11
  • stack 1
  • yesod 1

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor