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 :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-
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: