Haskell Cheat Sheet
Overview
Haskell is a purely functional programming language with strong static typing, type inference, and lazy evaluation. Named after logician Haskell Curry, the language was first specified in 1990 and has since become the benchmark for functional programming language design. Haskell’s type system, featuring type classes, algebraic data types, and higher-kinded types, enables developers to write expressive, correct, and maintainable code with powerful compile-time guarantees.
Haskell is used in production at companies like Facebook (spam filtering), Standard Chartered (financial modeling), GitHub (Semantic code analysis), and Mercury (banking). The language excels at compiler construction, formal verification, data analysis, and any domain where correctness is paramount. GHC (Glasgow Haskell Compiler) is the primary compiler, providing extensive optimizations and a rich ecosystem of extensions.
Installation
Using GHCup (Recommended)
# Install GHCup (manages GHC, cabal, Stack, HLS)
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
# Install specific GHC version
ghcup install ghc 9.8.1
ghcup set ghc 9.8.1
# Install build tools
ghcup install cabal 3.10.2.1
ghcup install stack 2.13.1
# Verify
ghc --version
cabal --version
stack --version
macOS with Homebrew
brew install ghcup
ghcup install ghc cabal stack hls
Core Language
Types and Values
| Type | Example | Description |
|---|---|---|
Int | 42 | Fixed-precision integer |
Integer | 999999999999 | Arbitrary precision integer |
Double | 3.14 | Double-precision float |
Bool | True, False | Boolean |
Char | 'a' | Single character |
String | "hello" | List of Char ([Char]) |
[a] | [1, 2, 3] | Homogeneous list |
(a, b) | (1, "hi") | Tuple |
Maybe a | Just 5, Nothing | Optional value |
Either a b | Left "err", Right 42 | Sum type |
Functions
-- Function definition with type signature
add :: Int -> Int -> Int
add x y = x + y
-- Pattern matching
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
-- Guards
bmi :: Double -> String
bmi x
| x < 18.5 = "underweight"
| x < 25.0 = "normal"
| x < 30.0 = "overweight"
| otherwise = "obese"
-- Where clause
circleArea :: Double -> Double
circleArea r = pi * r ^ 2
where pi = 3.14159265
-- Let expression
cylinderVolume :: Double -> Double -> Double
cylinderVolume r h =
let baseArea = pi * r ^ 2
pi = 3.14159265
in baseArea * h
-- Lambda
double :: [Int] -> [Int]
double = map (\x -> x * 2)
-- Function composition
processName :: String -> String
processName = reverse . map toUpper . filter isAlpha
-- Point-free style
sumOfSquares :: [Int] -> Int
sumOfSquares = sum . map (^ 2)
Lists
-- List construction
xs = [1, 2, 3, 4, 5]
ys = 0 : xs -- Cons: [0,1,2,3,4,5]
zs = [1..10] -- Range: [1,2,3,...,10]
evens = [2,4..20] -- Step range: [2,4,6,...,20]
-- List comprehension
squares = [x^2 | x <- [1..10], even x]
-- [4, 16, 36, 64, 100]
pairs = [(x,y) | x <- [1..3], y <- ['a'..'c']]
-- Common functions
head [1,2,3] -- 1
tail [1,2,3] -- [2,3]
take 2 [1,2,3] -- [1,2]
drop 2 [1,2,3] -- [3]
length [1,2,3] -- 3
reverse [1,2,3] -- [3,2,1]
zip [1,2] ["a","b"] -- [(1,"a"),(2,"b")]
elem 3 [1,2,3] -- True
-- Higher-order functions
map (+1) [1,2,3] -- [2,3,4]
filter even [1..10] -- [2,4,6,8,10]
foldl (+) 0 [1,2,3] -- 6
foldr (:) [] [1,2,3] -- [1,2,3]
concatMap (\x -> [x, x*2]) [1,2,3] -- [1,2,2,4,3,6]
Algebraic Data Types
-- Sum type
data Shape
= Circle Double
| Rectangle Double Double
| Triangle Double Double Double
deriving (Show, Eq)
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
area (Triangle a b c) =
let s = (a + b + c) / 2
in sqrt (s * (s-a) * (s-b) * (s-c))
-- Record syntax
data Person = Person
{ firstName :: String
, lastName :: String
, age :: Int
} deriving (Show, Eq)
-- Parameterized types
data Tree a
= Leaf
| Node (Tree a) a (Tree a)
deriving (Show, Eq)
-- Newtype (zero-cost wrapper)
newtype Email = Email String deriving (Show, Eq)
Type Classes
-- Define a type class
class Describable a where
describe :: a -> String
-- Instance
instance Describable Shape where
describe (Circle r) = "Circle with radius " ++ show r
describe (Rectangle w h) = "Rectangle " ++ show w ++ "x" ++ show h
describe (Triangle _ _ _) = "Triangle"
-- Common type classes
-- Eq: (==), (/=)
-- Ord: compare, (<), (>), min, max
-- Show: show (to String)
-- Read: read (from String)
-- Enum: succ, pred, [a..b]
-- Num: (+), (-), (*), abs, signum
-- Functor: fmap
-- Applicative: pure, (<*>)
-- Monad: (>>=), return
Monads and IO
-- IO Monad
main :: IO ()
main = do
putStrLn "What is your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")
-- Maybe Monad
safeDivide :: Double -> Double -> Maybe Double
safeDivide _ 0 = Nothing
safeDivide x y = Just (x / y)
chainedComputation :: Maybe Double
chainedComputation = do
a <- safeDivide 10 2
b <- safeDivide a 3
safeDivide b 0.5
-- Either Monad for error handling
parseAge :: String -> Either String Int
parseAge s = case reads s of
[(n, "")] | n >= 0 && n < 150 -> Right n
_ -> Left "Invalid age"
-- State Monad
import Control.Monad.State
counter :: State Int Int
counter = do
n <- get
put (n + 1)
return n
Build Tools
Cabal
# Create project
cabal init --interactive
# Build and run
cabal build
cabal run my-app
cabal test
cabal repl # Start GHCi with project
# Dependencies
cabal update
cabal install --lib aeson
Stack
# Create project
stack new my-project
# Build and run
stack build
stack exec my-app
stack test
stack ghci # Start GHCi with project
# Install tools
stack install hlint
stack install ormolu
package.yaml (Stack)
name: my-project
version: 0.1.0.0
dependencies:
- base >= 4.7 && < 5
- text
- aeson
- containers
- bytestring
library:
source-dirs: src
executables:
my-app:
main: Main.hs
source-dirs: app
dependencies:
- my-project
tests:
my-test:
main: Spec.hs
source-dirs: test
dependencies:
- my-project
- hspec
Configuration
GHC Language Extensions
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
Advanced Usage
Generics and Aeson (JSON)
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import GHC.Generics
data User = User
{ userName :: String
, userAge :: Int
} deriving (Show, Generic)
instance ToJSON User
instance FromJSON User
-- Usage
encode (User "Alice" 30)
-- "{\"userName\":\"Alice\",\"userAge\":30}"
decode "{\"userName\":\"Bob\",\"userAge\":25}" :: Maybe User
-- Just (User {userName = "Bob", userAge = 25})
Concurrency
import Control.Concurrent
import Control.Concurrent.Async
-- Fork thread
main = do
forkIO $ putStrLn "Hello from thread"
threadDelay 1000000
-- Async for structured concurrency
main = do
(a, b) <- concurrently (fetchUrl url1) (fetchUrl url2)
print (a, b)
-- MVar for communication
main = do
mvar <- newEmptyMVar
forkIO $ putMVar mvar 42
result <- takeMVar mvar
print result
Troubleshooting
| Problem | Solution |
|---|---|
No instance for (Show ...) | Add deriving Show or write instance |
Ambiguous type variable | Add type annotation or use TypeApplications |
Could not match type | Check function signatures; use :t in GHCi |
| Space leak (memory) | Use strict fields (!), BangPatterns, or seq |
Prelude.head: empty list | Use pattern matching or Data.Maybe.listToMaybe |
| Cabal dependency hell | Use cabal.project freeze files; consider Stack |
| Slow compilation | Enable parallel compilation: -j4 flag |
| String performance | Use Text instead of String for real applications |