Skip to content

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

# 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

TypeExampleDescription
Int42Fixed-precision integer
Integer999999999999Arbitrary precision integer
Double3.14Double-precision float
BoolTrue, FalseBoolean
Char'a'Single character
String"hello"List of Char ([Char])
[a][1, 2, 3]Homogeneous list
(a, b)(1, "hi")Tuple
Maybe aJust 5, NothingOptional value
Either a bLeft "err", Right 42Sum 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

ProblemSolution
No instance for (Show ...)Add deriving Show or write instance
Ambiguous type variableAdd type annotation or use TypeApplications
Could not match typeCheck function signatures; use :t in GHCi
Space leak (memory)Use strict fields (!), BangPatterns, or seq
Prelude.head: empty listUse pattern matching or Data.Maybe.listToMaybe
Cabal dependency hellUse cabal.project freeze files; consider Stack
Slow compilationEnable parallel compilation: -j4 flag
String performanceUse Text instead of String for real applications