Why Haskell? A Gentle Introduction to Functional Programming

Haskell Logo

Haskell is the Chuck Norris of programming languages -- strong, robust, and utterly committed to immutability. It is a purely functional language with a tight type system and lazy evaluation strategy that lets you tackle infinite data structures without breaking a sweat.

If you have ever looked at a sorting algorithm and thought "this could be more mathematical", Haskell was made for you.


What is Haskell?

Haskell is a statically typed, purely functional programming language with roots in academic research from the 1980s. A committee led by researchers inspired by Haskell Curry (yes, the language is named after him) set out to build a language grounded in mathematical elegance and correctness.

Today it is used everywhere from financial systems and compilers to security research and web backends. Its influence is visible in features that have since been adopted by Rust, Scala, Swift, and even Python.


1. Pure Functional

In Haskell, functions have no side effects by default. Given the same input, a function always returns the same output. This property -- called referential transparency -- makes code dramatically easier to reason about, test, and parallelise.

-- Fibonacci sequence
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
-- Quicksort in 3 lines
quicksort :: Ord a => [a] -> [a]
quicksort []     = []
quicksort (x:xs) = quicksort [y | y <- xs, y <= x]
               ++ [x]
               ++ quicksort [y | y <- xs, y > x]

Compare that quicksort to an imperative implementation. Same algorithm, a fraction of the code.


2. Expressive and Concise Syntax

Haskell promotes a declarative style -- you describe what you want, not how to compute it. This leads to code that reads almost like mathematics.

-- List comprehension: squares of 1 to 10
squares :: [Int]
squares = [x * x | x <- [1..10]]
-- Function composition with (.)
-- Total length of all strings in a list
totalLength :: [String] -> Int
totalLength = sum . map length

Function composition with . is one of Haskell's most powerful features. You build complex transformations by chaining simple functions together -- no intermediate variables, no mutation.


3. Powerful Type System

Haskell's type system catches bugs at compile time that would only surface at runtime in dynamic languages. It is not just type checking -- it is type-directed development.

-- Safe head: no more runtime crashes on empty lists
safeHead :: [a] -> Maybe a
safeHead []    = Nothing
safeHead (x:_) = Just x
-- Polymorphic addition: works on Int, Float, Double...
add :: Num a => a -> a -> a
add x y = x + y

The Maybe type is Haskell's answer to null pointer exceptions. Instead of crashing, you get Nothing -- a value you are forced to handle explicitly. This single idea has inspired Option in Rust and Swift, and Optional in Java.


4. Laziness and Efficiency

Haskell uses lazy evaluation -- values are only computed when they are actually needed. This is what allows Haskell to work with infinite data structures without running out of memory.

-- Infinite Fibonacci sequence
-- Only computes as many values as you ask for
fibonacci :: [Integer]
fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)

-- Take the first 10
first10 :: [Integer]
first10 = take 10 fibonacci
-- [0,1,1,2,3,5,8,13,21,34]
-- Infinite list of primes using Sieve of Eratosthenes
primes :: [Integer]
primes = sieve [2..]
  where
    sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

-- First 5 primes
first5primes :: [Integer]
first5primes = take 5 primes
-- [2,3,5,7,11]

Laziness also enables efficient I/O streaming -- large files are read on demand rather than loaded entirely into memory.


5. High-Level Abstractions

Monads

Monads are one of Haskell's most famous (and most misunderstood) features. At their core, they are a pattern for chaining computations that may have effects -- like failure, state, or I/O.

-- Safe division using the Maybe monad
safeDivide :: Int -> Int -> Maybe Int
safeDivide _ 0 = Nothing
safeDivide x y = Just (x `div` y)

-- Chain two divisions safely
example :: Int -> Int -> Maybe Int
example x y = do
  q      <- safeDivide x y
  result <- safeDivide (q + 2) 5
  return (result * 10)

If any step returns Nothing, the whole chain short-circuits. No if-else chains, no null checks -- just clean sequential logic.

Type Classes

Type classes define behaviour that different types can implement -- similar to interfaces in other languages, but more powerful.

-- Custom equality for a Person type
data Person = Person { name :: String, age :: Int }

instance Eq Person where
  (Person n1 a1) == (Person n2 a2) = n1 == n2 && a1 == a2

person1 :: Person
person1 = Person "Alice" 25

person2 :: Person
person2 = Person "Bob" 30

-- False
example :: Bool
example = person1 == person2

Algebraic Data Types

Haskell lets you model your domain precisely using sum types and product types.

-- A shape is either a Circle or a Rectangle -- nothing else
data Shape = Circle Double | Rectangle Double Double

area :: Shape -> Double
area (Circle r)      = pi * r * r
area (Rectangle w h) = w * h

example :: Double
example = area (Circle 5.0)
-- 78.53...

The compiler enforces exhaustive pattern matching -- if you add a new shape, every function that handles shapes will warn you at compile time.


6. Ecosystem and Tooling

The Haskell ecosystem is mature and well-tooled.

Build tools: Cabal and Stack manage dependencies and ensure reproducible builds.

Package repositories: Hackage hosts over 16,000 packages. Stackage provides curated stable snapshots.

Editor support: HLS (Haskell Language Server) gives you completions, type hints, and inline errors in VS Code, Neovim, Emacs, and more.

LibraryPurpose
aesonJSON parsing and encoding
servantType-safe web API development
conduitStreaming data processing
lensFunctional data manipulation
parsec / attoparsecParser combinators
QuickCheckProperty-based testing
STMSoftware Transactional Memory
persistentDatabase persistence
warpHigh-performance web server
hmatrixLinear algebra

7. Real-World Applications

Haskell is not just an academic language -- it powers production systems.

ProjectWhat it is
GHCThe Glasgow Haskell Compiler -- written in Haskell
PandocUniversal document converter
ShellCheckShell script linter used by millions
XMonadTiling window manager
CardanoProof-of-stake blockchain
HledgerPlain-text accounting software
GitAnnexDistributed file management
HLSHaskell Language Server

Facebook uses Haskell internally for spam filtering. Standard Chartered uses it for financial modelling. IOHK built the entire Cardano blockchain in Haskell specifically for its correctness guarantees.


8. Community and Support

The Haskell community is welcoming and genuinely enthusiastic about the language. Good starting points:

  • Haskell Discourse -- main community forum
  • r/haskell -- active Reddit community
  • Haskell Weekly -- curated newsletter
  • ZuriHac and Haskell Symposium -- annual conferences

The community values correctness, thoughtful design, and good documentation. Questions at any level are welcome.


Getting Started

Install GHC and Cabal via GHCup -- the recommended toolchain manager:

curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

Then start a REPL:

ghci
-- Try it immediately
Prelude> map (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]

Prelude> filter even [1..20]
[2,4,6,8,10,12,14,16,18,20]

Resources


Haskell rewards patience. The first week is confusing. The second week, things click. By the third week you will find yourself annoyed at every other language for not having algebraic data types.

Happy Haskelling.