Haskell Stash

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 than array…?)
  • 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:

(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? (or cabal install cabal-install)
  • Have you cabal updated sufficiently recently?
  • Did you actually run cabal sandbox init? (although if you put require-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: ghc-mod (big package! pulls in hlint) && hdevtools && hoogle, for editor integration and command-line tools.

    If you want “midpoints” to install parts of the dependencies, consider haskell-src-exts then hlint then ghc-mod.
  • wreq-box: wreq (snappy high-level HTTP awesomeness with lenses) && haskeline (pretty much solely for getPassword)

    Midpoints: semigroupoids, then lens. I should really rigorously time this at some point.

    Things this pulls in:

    • cryptohash and base16-bytestring (and base64-bytestring), so I think this can also be my messing-with-cryptography box
    • attoparsec, 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)) && possibly hspec-expectations

    As of time of writing, arithmoi appears to have an overly harsh upper bound on random. 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 can git pull and cabal install from.

    (resolved!)

    hspec-expectations puts combinators like shouldBe in Test.Hspec.Expectations for drop-dead simple testing when you need it. This depends on HUnit 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)
-- }}}

(note: the commenting setup here is experimental and I may not check my comments often; if you want to tell me something instead of the world, email me!)