-
Notifications
You must be signed in to change notification settings - Fork 4
0098: Email Tutorials ‐ FP 12 ‐ Declaring Types and Classes
- Big Picture: What “declaring types” means in Haskell
-
typeDeclarations (Type Synonyms) 2.1 Why use them 2.2 Parameterized type synonyms 2.3 Limitations (why they can’t be recursive) -
dataDeclarations (Algebraic Data Types) 3.1 Constructors and what they really are 3.2 Pattern matching on constructors 3.3 Constructors with parameters - Parameterized Data Types:
Maybefor safe failure 4.1 Why “safe” functions matter 4.2safeDivandsafeHead - Recursive Data Types
5.1 Peano naturals (
Nat) 5.2 Conversions (natToInt,intToNat) 5.3 Recursive addition and multiplication - Tree-like Recursive Types: Expression Trees (
Expr) 6.1 Building expressions 6.2sizeandeval - Folding Over Expressions (
foldE) 7.1 Why folds matter 7.2 DefiningfoldE7.3 DefiningevalandsizeusingfoldE - Binary Trees with Data in Leaves (
Tree a) - (Bonus) Intro to Typeclasses and
deriving(practical basics) - Practice Exercises (with solutions)
So far, you used built-in types like Int, Bool, and lists. This lecture shows how to create your own types in two ways:
-
type: rename an existing type (synonym). -
data: create a brand new type with one or more constructors.
Think of it like this:
-
type= “alias” (same underlying type) -
data= “new structure” (new shape of values)
A type declaration introduces a new name for an existing type:
type String = [Char]This means String is not a new type at runtime; it’s just a friendlier name for [Char].
To make types more readable and meaningful:
type Pos = (Int, Int)
origin :: Pos
origin = (0,0)
left :: Pos -> Pos
left (x,y) = (x-1, y)Without Pos, you’d see (Int,Int) -> (Int,Int) everywhere, and it’s less clear that those pairs represent positions.
Type synonyms can take parameters:
type Pair a = (a,a)
copy :: a -> Pair a
copy x = (x,x)This is not allowed:
type Tree = (Int, [Tree]) -- ❌ not allowedRecursive “real structures” must be built with data, not type.
A data declaration creates a new type by listing constructors:
data Bool = False | TrueHere:
-
Boolis the type name -
FalseandTrueare constructors
This is a core mental model: constructors are functions that build data.
For example, with:
data Answer = Yes | No | UnknownYou can pattern match:
flipA :: Answer -> Answer
flipA Yes = No
flipA No = Yes
flipA Unknown = UnknownConstructors can carry data:
data Shape = Circle Float | Rect Float Float-
Circletakes oneFloatradius -
Recttakes twoFloats (width and height)
Compute area by pattern matching:
area :: Shape -> Float
area (Circle r) = pi * r^2
area (Rect x y) = x * ySome standard functions can crash:
- division by zero
- taking the head of an empty list
Maybe represents “this might fail”:
data Maybe a = Nothing | Just a-
Nothing= failure -
Just x= success and resultx
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv m n = Just (m `div` n)safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just xUsing Maybe forces the caller to handle failure safely.
data Nat = Zero | Succ NatValues look like:
-
Zero= 0 -
Succ Zero= 1 -
Succ (Succ Zero)= 2 - …
natToInt :: Nat -> Int
natToInt Zero = 0
natToInt (Succ n) = 1 + natToInt nintToNat :: Int -> Nat
intToNat 0 = Zero
intToNat n = Succ (intToNat (n-1))add :: Nat -> Nat -> Nat
add Zero n = n
add (Succ m) n = Succ (add m n)Multiplication as repeated addition:
mult :: Nat -> Nat -> Nat
mult Zero _ = Zero
mult (Succ n) m = add (mult n m) mTermination intuition: each recursive step reduces the first Nat toward Zero.
data Expr = Val Int | Add Expr Expr | Mul Expr ExprExpression: 1 + (2 * 3) becomes:
e :: Expr
e = Add (Val 1) (Mul (Val 2) (Val 3))Count how many numbers (Val) occur:
size :: Expr -> Int
size (Val _) = 1
size (Add x y) = size x + size y
size (Mul x y) = size x + size yEvaluate numerically:
eval :: Expr -> Int
eval (Val n) = n
eval (Add x y) = eval x + eval y
eval (Mul x y) = eval x * eval yA fold captures the “primitive recursion pattern” for a structure.
For lists, foldr replaces (:) and [].
For expressions, foldE replaces Val, Add, and Mul.
foldE :: (Int -> a) -> (a -> a -> a) -> (a -> a -> a) -> Expr -> a
foldE fVal fAdd fMul = go
where
go (Val n) = fVal n
go (Add x y) = fAdd (go x) (go y)
go (Mul x y) = fMul (go x) (go y)eval :: Expr -> Int
eval = foldE id (+) (*)size :: Expr -> Int
size = foldE (const 1) (+) (+)This is a big moment: once you have a fold, many functions become one-liners.
data Tree a = Leaf a | Node (Tree a) (Tree a)-
Leaf astores a value -
Node left rightstores two subtrees
Example tree:
t :: Tree Int
t = Node (Leaf 1) (Node (Leaf 2) (Leaf 3))Even though your transcript focused more on type/data, the chapter title mentions “types and classes”. Here’s the most practical piece you’ll use immediately:
If you do:
data Answer = Yes | No | UnknownGHC might not automatically know how to print it nicely, compare it, etc.
data Answer = Yes | No | Unknown
deriving (Eq, Show)-
Showlets you print values in GHCI. -
Eqlets you use==and/=.
Example:
Yes == No -- False
show Unknown -- "Unknown"You’ll see deriving (Eq, Ord, Show, Read) a lot.
Write a safe tail that fails on empty list:
safeTail :: [a] -> Maybe [a]
safeTail [] = Nothing
safeTail (_:xs) = Just xsCount how many Add/Mul nodes appear:
ops :: Expr -> Int
ops = foldE (const 0) (\a b -> 1 + a + b) (\a b -> 1 + a + b)leafCount :: Tree a -> Int
leafCount (Leaf _) = 1
leafCount (Node l r) = leafCount l + leafCount r-
Type: A classification of values (e.g.,
Int,Bool,[Char]). -
Declare a type: In Haskell you declare types (not “define” them), using
typeordata. -
Type synonym: A nickname for an existing type (made with
type). -
Algebraic Data Type (ADT): A custom type defined by listing its possible shapes/values (made with
data). -
Constructor: A function that builds a value of a
datatype (e.g.,Just,Nothing,Leaf,Node). -
Pattern matching: Deconstructing values by matching on their constructors (e.g.,
(x:xs),Just x,Node l r). -
Parameterized type: A type that takes a type parameter (e.g.,
Maybe a,Tree a). -
Recursive type: A type that refers to itself in its definition (e.g.,
data Nat = Zero | Succ Nat). - Fold: A higher-order function that captures a common recursion pattern, replacing constructors with functions.
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: