-
Notifications
You must be signed in to change notification settings - Fork 4
0102: Email Tutorials – AFP 5 — Functors
- Email 1 – The Big Question: Pure or Impure?
- Email 2 – Why “Effects” Matter in Real Programs
- Email 3 – A Pattern Hiding in Plain Sight:
incandsquare - Email 4 – Abstracting the Pattern: Rediscovering
map - Email 5 – Generalising Beyond Lists: Why We Need Functors
- Email 6 – The Functor Typeclass:
fmapand Type Constructors - Email 7 – Functor Instance #1: Lists (
[]) - Email 8 – Functor Instance #2:
Maybe(Propagating Failure) - Email 9 – Functor Instance #3: A Binary
Tree - Email 10 – Why Functors Are Useful: One Name, Many Structures
- Email 11 – The Superpower: Generic Functions for Any Functor
- Email 12 – Functor Laws: What Must Always Be True
- Glossary of Terms
- Quiz Questions (Table Format)
- Quiz Options (Table Format)
- Next Steps and Quiz Link
This lecture starts with a simple but deep question: should programs be pure or impure? In programming terms, pure means your programs behave like mathematical functions: they take inputs, produce outputs, and don’t secretly interact with the outside world in the middle. Impure means programs can perform side effects like reading input, writing output, mutating state, or throwing exceptions.
This question matters because it shapes how you write code, test code, and reason about correctness. Pure code is easier to reason about and refactor safely, because “same input ⇒ same output” becomes a reliable mental rule.
Even if purity is beautiful, real software must interact with the world:
- read user input
- write files
- call APIs
- handle failures
- manage state
So the real goal is: combine purity’s reasoning benefits with effectful programming. The lecture hints at where this is going: monads are the course’s answer (coming later). Functors are a key stepping stone because they teach you how to structure computations inside containers and contexts.
The lecture uses two list functions to expose a repeated programming pattern.
inc :: [Int] > [Int]
inc [] = []
inc (n:ns) = (n + 1) : inc nssquare :: [Int] > [Int]
square [] = []
square (n:ns) = (n * n) : square nsThese look different, but structurally they are the same:
- base case: empty list maps to empty list
- recursive case: transform head, recurse on tail
That “same shape, different headtransform” is exactly the signal that abstraction is possible.
Once you spot the pattern, you extract it into a single reusable tool: map.
map' :: (a > b) > [a] > [b]
map' f [] = []
map' f (x:xs) = f x : map' f xsNow the original functions become instances of the pattern:
inc2 :: [Int] > [Int]
inc2 = map (+1)
square2 :: [Int] > [Int]
square2 = map (\n > n * n)This is the lecture’s first big theme: abstracting common recursion patterns.
The next leap is: mapping isn’t just for lists. You often want to “map over”:
- optional values (
Maybe) - trees
- other data structures that “contain” values
So the lecture generalises the idea: a functor is anything you can map over.
Haskell captures “things you can map over” using a typeclass:
class Functor f where
fmap :: (a > b) > f a > f bKey idea: f is not a normal type like Int.
f is a type constructor (a parameterised type), like:
[]MaybeTree
So fmap means:
“Given a function
a > b, transform values inside anf ato get anf b, without destroying the structure.”
Lists are the easiest functor: fmap is just map.
instance Functor [] where
fmap = mapSo these are equivalent:
map (+1) [1,2,3]
fmap (+1) [1,2,3]Recall the Maybe type:
data Maybe a = Nothing | Just aIts functor instance:
instance Functor Maybe where
fmap g Nothing = Nothing
fmap g (Just x) = Just (g x)Meaning:
- If computation “failed” (
Nothing), mapping keeps it failed. - If computation succeeded (
Just x), mapping transforms the value.
fmap (+1) Nothing
Nothing
fmap (*2) (Just 3)
Just 6Define trees with values at leaves:
data Tree a
= Leaf a
| Node (Tree a) (Tree a)
deriving (Show)Functor instance:
instance Functor Tree where
fmap g (Leaf x) = Leaf (g x)
fmap g (Node l r) = Node (fmap g l) (fmap g r)This preserves shape and transforms contents.
fmap even (Node (Leaf 1) (Leaf 2))
Node (Leaf False) (Leaf True)Two big benefits from the lecture:
-
Same name for the same idea Instead of
mapList,mapMaybe,mapTree, you usefmapeverywhere. When you seefmap, you immediately read: “mapping inside something”. -
A shared interface enables reusable tools Once many types are functors, you can write helper functions once.
The lecture’s punchline: the old “increment everything” can be made fully generic.
incF :: Functor f => f Int > f Int
incF = fmap (+1)Now it works on lists, Maybe, and trees:
incF [1,2,3]
[2,3,4]
incF (Just 1)
Just 2
incF (Node (Leaf 1) (Leaf 2))
Node (Leaf 2) (Leaf 3)This is a major mindset shift: write programs that target capabilities (typeclasses), not concrete datatypes.
A proper functor must obey two laws:
fmap id = idfmap (g . f) = fmap g . fmap fThese laws are why you can refactor confidently: mapping “behaves like mapping” everywhere, not just in your examples.
Pure Language A language where programs behave like mathematical functions (no side effects).
Impure Language A language where programs can perform side effects directly (I/O, state, exceptions).
Side Effect Any interaction beyond returning a value (printing, reading input, mutation, exceptions).
HigherOrder Function A function that takes a function as an argument and/or returns a function.
Map A function that applies another function to each element of a list.
Functor A typeclass for structures you can map over with fmap.
Type Constructor A parameterised type like Maybe or [] or Tree.
fmap The functor mapping function: transforms values inside a structure.
Shape Preservation fmap changes values but keeps the outer structure (list length, tree shape, etc.).
Functor Laws Rules (identity, composition) that make functors consistent and predictable.
Bernard Sibanda is a global Technology Entrepreneur, Web3 and Software Consultant with a deep focus on Cardano Blockchain, Midnight and Community building.
Key Positions:
- Founder, CTO, Developer Advocate cohort #1, Fullstake Developer, Cardano Ambassador, Catalyst Project Manager, DREP-WIMS:
- Co-founder of ABL Tech and Cardano Africa Live
- EBU-certified Plutus Pioneer (Plutus/Haskell)
- Cohort #1 Plutus Pioneer Developer
- Catalyst Community Reviewer & Funded Projects Manager
-
DRep for WIMS-Cardano (ID:
drep1yguj8zu48n99pv70yl6ckzt9hdgjy8yjnlqs2uyzcpafnjgu4vkul) - Intersect Developer Advocate
- Intersect Committe Member 2025-2026
- Cardano Marketer,Promoter and blogger
- Cardano Open Source Contributor
- Cardano communities and events organizer and builder
- Cardano Ambassador for South Africa
Official links:
- Stablecoins Dex
- Coxygen Global Universities
- WIMS Cardano Global
- Cardano Africa Live
- WIMS Cardano Videos
- Cardano Smart Contract Videos
- Fullstack IT Consulting
Social links: