Editor’s note: This is almost certainly years out of date. cabal has v2 commands and stuff now? Sorry.
Installation Notes
Personal list of Hackage packages to install, in approximate decreasing order of priority.
In Haskell Platform
Changelog (in case there was any doubt)
GHC notably has array
, bytestring
, template-haskell
, transformers
.
Additional libraries notably include attoparsec
, HTTP
, HUnit
, mtl
, parsec
, QuickCheck
, text
, vector
.
Still, come to think of it, these libraries may well have updated since the platform release…
I’m too lazy to reinstall all of them, so here are the ones I recognize that I care about, minus containers
because it mysteriously threatens to break ghc-7.8.3
itself, plus the update
that I always forget:
cabal update
cabal install HTTP HUnit QuickCheck array attoparsec bytestring mtl parsec random split text transformers unordered-containers vector
Of those, I’d say these are particularly important:
vector
(better thanarray
…?)parsec
(parsing with Haskell is awesome)
Bare GHC?
After asking around on #haskell, I learned https://ghcformacosx.github.io/, which is easy to relocate doesn’t come with those default extra slightly-out-of-date packages, only the basic GHC and cabal-install.
Cabal Preparation
Go to ~/.cabal/config
and change these settings:
verbose: 3
documentation: True
require-sandbox: True
Verbosity helps keep me sane when Cabal seems screwy or lagging.
Sometimes Cabal generates Haddock documentation explicitly without me saying so, sometimes it doesn’t. Better safe than sorry.
And I’ve been holding out on requiring Cabal to install into sandboxes because I feel like global installs are still sometimes useful, but I am constantly being bitten by forgetting to install in a sandbox, and haven’t wanted to install something globally for a long time. Should you ever really want to install something globally, cabal
will tell you how to override that option when you try: use --no-require-sandbox
. But it won’t tell you where to use --no-require-sandbox
: you should put it as your first flag, right after cabal
and before even the subcommand, according to this StackOverflow answer and personal experience.
cabal install cabal-install
for the newest version.
Then, restart your terminal. Make sure cabal --version
says what it should.
Also, apparently you should install happy
and alex
unsandboxed.
cabal sandbox
I took too long to figure this out.
Pick a nice folder or create one, cd
there, and then:
Now cabal
will use packages in the sandbox for everything. But for my use case, I only want access to certain libraries for quick scripts without bringing the rest of my installation crashing down into dependency hell.
That means I’ll ghc
or runhaskell
them, which does not automatically use the sandbox. Instead, I can:
Or even do this to get a brand-new shell where ghc
and runhaskell
all know about the sandbox:
More full-blown:
Somewhat strangely, however, cabal install
doesn’t work inside the cabal exec sh
. Oh well.
So, I think I’ll try to avoid entering a shell. It took a little searching but finally I found cabal sandbox tips for some ways to get cabal to “stay with you”. The weird ${a+b}
syntax is one of various parameter substitution syntaxes. Eventually I cobbled this together:
export PS1='${sandbox_name+\[\e[0;31m\][box $sandbox_name]}\[\e[1;32m\]\u\[\e[0m\]@ \[\e[0;34m\]\W\[\e[0m\]\$ '
function uncabalize() {
echo "uncabalizing..."
unset sandbox_name
unset CABAL_SANDBOX_CONFIG
}
function cabalize() {
if [[ -e cabal.sandbox.config ]]; then
echo "cabalizing to `basename $PWD`..."
export sandbox_name=`basename $PWD`
export CABAL_SANDBOX_CONFIG=$PWD/cabal.sandbox.config
else
uncabalize
fi
}
(Note that this has to be like a function in .profile
because scripts can’t set environment variables seen by the parent shell.)
Now all that’s necessary is to make aliases for cabal exec ghc
or cabal exec runhaskell
.
Alternatively, if you want a shell script or alias or whatever for a specific setup for ghci
:
Vim interface haxx
Incohesive and undocumented. Use at your own risk.
function! s:get_cabal_sandbox_g_option()
let l:config_file = ""
if !empty($CABAL_SANDBOX_CONFIG)
let l:config_file = $CABAL_SANDBOX_CONFIG
elseif filereadable('cabal.sandbox.config')
let l:config_file = 'cabal.sandbox.config'
endif
if !empty(l:config_file)
let l:output = system("grep package-db < '" . l:config_file . "'")
let l:dir = matchstr(substitute(l:output, '\n', ' ', 'g'), 'package-db: \zs\S\+\ze')
return "-g-package-db='" . l:dir . "'"
else
return ''
endif
endfunction
let g:syntastic_haskell_ghc_mod_args = s:get_cabal_sandbox_g_option() . " check --boundary=' '"
let g:syntastic_haskell_hdevtools_args = s:get_cabal_sandbox_g_option() . " -g-Wall"
Note that ghc-mod
and hdevtools
actually kind of fill the same niche, so I think you only need one of them. Pick whichever one you get working. hdevtools
’s advantage is its client-server architecture means it can keep a background process alive and reload more quickly.
My Sandboxes
Link: Haskell Best Practices for Avoiding “Cabal Hell”
Preparatory Checklist
For paranoid triple-checking, before you install anything:
- Is the
cabal
version new enough? (orcabal install cabal-install
) - Have you
cabal update
d sufficiently recently? - Did you actually run
cabal sandbox init
? (although if you putrequire-sandbox: True
the computer will remember this for you)
Installation Troubleshooting
Useful commands:
cabal get
?cabal sandbox add-source path/to/folder/with/module
, if you need to make cabal install from a local folder, instead of from something uploaded to hackage. In the most common case, this means you find a git repo, clone it, and put the path of the cloned folder. Maybe you need it because the old maintainer disappeared and you need a third-party fix, or maybe you just happen to be messing with it in the interim between when a fix is committed and when it’s uploaded, or maybe you want to fix it yourself.cabal install some-package --allow-newer
to give upper bounds the middle finger and try to install anyway--dry-run
to see what cabal will try to install
Actually, Sandboxes
ghc-mod-box:
If you want “midpoints” to install parts of the dependencies, considerghc-mod
(big package! pulls inhlint
) &&hdevtools
&&hoogle
, for editor integration and command-line tools.haskell-src-exts
thenhlint
thenghc-mod
.wreq-box:
wreq
(snappy high-level HTTP awesomeness with lenses) &&haskeline
(pretty much solely forgetPassword
)Midpoints:
semigroupoids
, thenlens
. I should really rigorously time this at some point.Things this pulls in:
cryptohash
andbase16-bytestring
(andbase64-bytestring
), so I think this can also be my messing-with-cryptography boxattoparsec
,lens
(duh),semigroups
,semigroupoids
…
I may also add
JuicyPixels
here, to generalize it to being the master web-interaction exploitation capture-the-flag box.eulerbox:
data-memocombinators
&&base-unicode-symbols
&&arithmoi
(hey, look, number-theoretic stuff, why reinvent the wheel? (Although I already have and in a certain sense it’s part of the point of Project Euler)) && possiblyhspec-expectations
As of time of writing,arithmoi
appears to have an overly harsh upper bound onrandom
. There are even two pull request #5 #6 trying to rectify this, but they’re three months old and haven’t been pulled, sadly. In the meantime, there is a nice bugfix fork that you cangit pull
andcabal install
from.(resolved!)
hspec-expectations
puts combinators likeshouldBe
inTest.Hspec.Expectations
for drop-dead simple testing when you need it. This depends onHUnit
but that’s somehow already in the sandbox.idris
(dependent typing FTW)
I guess hspec
(colorful tests FTW) should go in a project-local sandbox.
Packages Pending Investigation
classy-prelude
(I’m quite ambivalent right now because the dependencies are huge for something I want in quick-and-dirty scripts)pointfree
(I don’t actually use this, although come to think of it, there are probably times when I would have benefited but brushed it off)criterion
(benchmarking. I should learn this some day)
exec vs eval
A silly mnemonic paragraph. A lot of monad usages end with a “return”. Somebody who “turn”s is an evil disloyal person.
- Use
evalState
if you want the evil re“turn”er. - Use
execState
if you want to “execute” the criminal and just leave the state of his destruction. - Obviously, the person’s life is still more important than destruction he/she caused, so
runState
returns the person and then the state.
This also applies to many monads similar to State
.
template
-- @betaveros :: vim:set fdm=marker:
{-# LANGUAGE LambdaCase, NPlusKPatterns, TupleSections #-}
{-# OPTIONS_GHC -fno-warn-unused-imports -fno-warn-missing-signatures #-}
-- import ALL the things! {{{
-- hiding clauses are to allow Data.Foldable's generalizations
import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl, foldl1, foldr, foldr1, and, or, any, all, sum, product, concat, concatMap, maximum, minimum, elem, notElem)
import Control.Applicative
import Control.Arrow
import Control.Exception
import Control.Monad hiding (mapM, mapM_, forM, forM_, sequence, sequence_, msum)
import Control.Monad.ST
import qualified Data.ByteString.Char8 as BS
import Data.ByteString.Char8 (ByteString)
import Data.Bits
import Data.Char
import Data.Either
import Data.Foldable
import Data.Function
import Data.IORef
import Data.List hiding (foldl, foldl', foldl1, foldl1', foldr, foldr1, concat, concatMap, and, or, any, all, sum, product, maximum, minimum, elem, notElem, find)
import Data.Maybe
import Data.Monoid
import Data.Ord
import Data.STRef
import Data.String
import Data.Traversable
import Data.Tuple
import qualified Data.Map as Map
import Data.Map (Map)
import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Sequence as Seq
import Data.Sequence (Seq, (<|), (|>), (><))
import Debug.Trace
import Text.Printf
import System.IO
-- }}}
-- silly utilities {{{
-- stolen from lens (note (&) is in Data.Function in 7.10):
a & f = f a
a <&> f = fmap f a
infixl 1 &, <&>
fI :: (Integral a, Num b) => a -> b
fI = fromIntegral
gLen :: (Num b) => [a] -> b
gLen = genericLength
readInt = read :: String -> Int
readInteger = read :: String -> Integer
-- (!?) :: (Ord k) => Map k v -> k -> Maybe v
-- (!?) = flip Map.lookup
histogram :: (Ord a, Num b) => [a] -> Map a b
histogram = Map.fromListWith (+) . map (,1)
-- }}}
-- input and output {{{
bsGetLine :: IO ByteString
bsGetLine = fst . BS.spanEnd isSpace <$> BS.getLine
inputInt = (read <$> getLine) :: IO Int
inputInteger = (read <$> getLine) :: IO Integer
inputDouble = (read <$> getLine) :: IO Double
inputRow :: (Read a) => IO [a]
inputRow = map read . words <$> getLine
inputInts = inputRow :: IO [Int]
inputIntegers = inputRow :: IO [Integer]
inputDoubles = inputRow :: IO [Double]
ssUnwords :: [ShowS] -> ShowS
ssUnwords [] = id
ssUnwords (x:xs) = x . (' ':) . ssUnwords xs
ssUnlines :: [ShowS] -> ShowS
ssUnlines [] = id
ssUnlines (x:xs) = x . ('\n':) . ssUnlines xs
showMany :: (Show a) => [a] -> String
showMany xs = ssUnwords (map shows xs) ""
showMatrix :: (Show a) => [[a]] -> String
showMatrix xs = ssUnlines (map (ssUnwords . map shows) xs) ""
printMany :: (Show a) => [a] -> IO ()
printMany xs = putStrLn (showMany xs)
printMatrix :: (Show a) => [[a]] -> IO ()
printMatrix xs = putStr (showMatrix xs)
-- }}}