Haskell Stash

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:

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:

$ cabal sandbox init
$ cabal install whatever

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:

$ cabal exec runhaskell myscript.hs

Or even do this to get a brand-new shell where ghc and runhaskell all know about the sandbox:

$ cabal exec sh

More full-blown:

$ cabal exec sh -- --login

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:

ghci -package-db=/path/to/sandbox/whatever-packages.conf.d -ghci-script=/path/to/ghci/script/whatever.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:

Installation Troubleshooting

Useful commands:

Actually, Sandboxes

I guess hspec (colorful tests FTW) should go in a project-local sandbox.

Packages Pending Investigation

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.

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)
-- }}}