An example
def sum(numbers: List[int]) -> int:
result = 0
for number in numbers:
result = result + number
return result
>>> sum([1,2,3])
6
>>> sum([10])
10
>>> sum([])
0
def sum(numbers: List[int]) -> int:
if (numbers == []):
return 0
head: int = numbers[0]
tail: [int] = numbers[1:]
return head + sum(tail)
>>> sum([1,2,3])
6
>>> sum([10])
10
>>> sum([])
0
def sum(numbers: List[int]) -> int:
if (numbers == []):
return 0
head: int = numbers[0]
tail: [int] = numbers[1:]
return head + sum(tail)
Base Case
Self-reference/ recursive call
sum :: [Int] -> Int
sum [] = 0
sum x:xs = x + (sum xs)
Haskell edition
sum :: [Int] -> Int
sum [] = 0
sum x:xs = x + (sum xs)
Base Case
Self-reference/ recursive call
Haskell edition
An example
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
data Natural =
One
| Succ Natural
Represent natural numbers (positive integers)
Example: (Python)
one: Nat = 1
# succ(nat: Nat) -> Nat
succ(one) == 2
succ(succ(one)) == 3
data Natural =
One
| Succ Natural
natOne = One
natTwo = Succ One
natThree = Succ (Succ One)
natFour = Succ (Succ (Succ One))
alsoNatFour = Succ (natThree)
Represent natural numbers (positive integers)
Example: (Haskell)
data List a =
Empty
| Cons a (List a)
This list is created by "cons-ing" onto the head of the list
Example: (Python)
num: int = 1
nums: List[int] = [2,3]
empty: List[Int] = []
# cons(head: int, tail: List[int]) -> List[int]
cons(num, empty) == [1]
cons(num, nums) == [1,2,3]
cons(num, cons(num, nums)) == [1,1,2,3]
data List a =
Empty
| Cons a (List a)
listOne = Cons 1 Empty
listOneTwo = Cons 1 (Cons 2 Empty)
listOneToThree = Cons 1 (Cons 2 (Cons 3 Empty))
listOneToFour = Cons 1 (Cons 2 (Cons 3 (Cons 4 Empty)))
listOneToFour' = 1 : 2 : 4 : []
listOneToFour'' = [1, 2, 3, 4]
This list is created by "cons-ing" onto the head of the list
Example: (Haskell)
def concat_strings(strings: List[str]) -> str:
return foldr_list(
operation=lambda a, b: a + b,
base_case="",
elements=strings)
>>> concat_strings(["Man", " it's", " a", " hot", " one"])
"Man it's a hot one"
A = TypeVar('A'); B = TypeVar('B')
def foldr_list(operation: Callable[[A, B], B],
base_case: B,
elements: List[A]
) -> B:
if (elements == []):
return base_case
head: A = elements[0]
tail: List[A] = elements[1:]
return operation(head, foldr_list(operation, base_case, tail))
-- Simplified right fold
foldrList :: (a -> b -> b) -> b -> [a] -> b
foldrList operation baseCase [] = baseCase
foldrList operation baseCase (x:xs) =
operation x (foldrList operation baseCase xs)
λ> concatStrings = foldrList (++) ""
λ> concatStrings ["Spanish", " Harlem", " Mona", " Lisa"]
"Spanish Harlem Mona Lisa"
λ> concatStrings []
""
sum :: [Int] -> Int
sum numbers = foldr (+) 0 numbers
λ> sum [1,2,3]
6
λ> sum [10]
10
λ> sum []
0
sum :: [Int] -> Int
sum numbers = foldr (+) 0 numbers
Base Case
Self-reference/ recursive call
foldrList :: (a -> b -> b) -> b -> [a] -> b
foldrList f baseCase [x1, x2, x3]
=
----------------------------------
: -> f
/ \ / \
x1 : -> x1 f
/ \ / \
x2 : -> x2 f
/ \ / \
x3 [] -> x3 baseCase
sum :: [Int] -> Int
sum numbers = foldr (+) 0 numbers
sum [1, 2, 3]
=
------------------------
: -> +
/ \ / \
1 : -> 1 +
/ \ / \
2 : -> 2 +
/ \ / \
3 [] -> 3 0
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
data Natural =
One
| Succ Natural
| = One
/ \
One Succ (|) = Succ One
/ \
One Succ (|) = Succ (Succ One)
/ \
One Succ (|) = Succ (Succ (Succ One))
/ \
One Succ (|) = Succ (Succ (Succ (Succ One)))
/ \
One ... = Succ (Succ (Succ (Succ ...)))
| = Empty
/ \
E Cons
1 (|) = Cons 1 Empty
/ \
E Cons
2 (|) = Cons 1 (Cons 2 Empty)
/ \
E Cons
3 (|) = Cons 1 (Cons 2 (Cons 3 Empty))
/ \
E Cons
4 (|) = Cons 1 (Cons 2 (Cons 3 (Cons 4 Empty)))
/ \
E Cons
5 (...) = Cons 1 (Cons 2 (Cons 3 (Cons 4 (Cons 5 ...))))
data List a =
Empty
| Cons a (List a)
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
What's similar?
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
Both have a "unit" value
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
Both have a recursive value
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
data Recursive a =
Unit
| Ctor (Recursive a)
?
Recursive can either be a "unit" value or a recursively defined value
?
data Recursive a =
Unit
data Natural =
One
data List a =
Empty
?=
one = One :: Natural
empty = Empty :: List Int
rOne = Unit :: Recursive ()
rEmpty = Unit :: Recursive Int
?
data Recursive a =
Unit
data Natural =
One
data List a =
Empty
one = One :: Natural
empty = Empty :: List Int
rOne = Unit :: Recursive ()
rEmpty = Unit :: Recursive Int
data Recursive a =
Unit
data Natural =
One
data List a =
Empty
==
data Natural =
Succ Natural
data List a =
Cons a (List a)
data Recursive a =
Ctor (Recursive a)
?
rplusOne r = Ctor r :: Recursive ()
rconsOne r = Ctor r :: Recursive Int
rplusTwo r = Ctor (Ctor r)
rconsOneTwo r = Ctor (Ctor r)
plusOne n = Succ n :: Natural
consOne t = Cons 1 t :: List Int
plusTwo n = Succ (Succ n)
consOneTwo t = Cons 1 (Cons 2 t)
?=
data Natural =
Succ Natural
data List a =
Cons a (List a)
data Recursive a =
Ctor (Recursive a)
?
rplusOne r = Ctor r :: Recursive ()
rconsOne r = Ctor r :: Recursive Int
^?^
rplusTwo r = Ctor (Ctor r)
rconsOneTwo r = Ctor (Ctor r)
^?^ ^?^
plusOne n = Succ n :: Natural
consOne t = Cons 1 t :: List Int
^
plusTwo n = Succ (Succ n)
consOneTwo t = Cons 1 (Cons 2 t)
^ ^
!=
data Natural =
Succ Natural
data List a =
Cons a (List a)
data Recursive a =
Ctor (Recursive a)
?
data Recursive a =
Unit
| Ctor (Maybe a) (Recursive a)
?
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
data Recursive a =
Unit
| Ctor (Maybe a) (Recursive a)
type Nat = Recursive ()
goodOne = Unit
goodTwo = Ctor Nothing Unit
goodThree = Ctor Nothing (Ctor Nothing Unit)
?
Can we make a Natural?
data Recursive a =
Unit
| Ctor (Maybe a) (Recursive a)
type Nat = Recursive ()
goodOne = Unit
-- = One
goodTwo = Ctor Nothing Unit
-- = Succ One
goodThree = Ctor Nothing (Ctor Nothing Unit)
-- = Succ (Succ One)
Can we make a Natural?
YES
data Recursive a =
Unit
| Ctor (Maybe a) (Recursive a)
type List a = Recursive a
goodEmpty = Unit
goodOne = Ctor (Just 1) Unit
goodOneTwo = Ctor (Just 1) (Ctor (Just 2) Unit)
?
Can we make a List?
data Recursive a =
Unit
| Ctor (Maybe a) (Recursive a)
type List a = Recursive a
goodEmpty = Unit
-- = Empty
goodOne = Ctor (Just 1) Unit
-- = Cons 1 Empty
goodOneTwo = Ctor (Just 1) (Ctor (Just 2) Unit)
-- = Cons 1 (Cons 2 Empty)
Can we make a List?
YES
data Recursive a =
Unit
| Ctor (Maybe a) (Recursive a)
So we can make a natural and list!
But something is wrong...
type Nat = Recursive (Recursive Int)
badNat :: Nat
badNat = Ctor (Ctor (Just 666) Unit) Unit
type IntList = Recursive Int
badList :: IntList
badList = Ctor Nothing (Ctor (Just 1) Unit)
badEmpty :: IntList
badEmpty = Ctor Nothing (Ctor Nothing Unit)
🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
data Natural =
One
| Succ Natural
data List a =
Empty
| Cons a (List a)
data Recursive w a =
Unit
| Ctor (w (Recursive w a))
?
-- |List wrapper w/ Label!
data ListNode a r = ListNode
{ nodeValue :: a
, tailNodes :: r
}
-- |Nat wrapper w/o Label!
data NatNode r = NatNode
{ tailNodes :: r
}
type List a = Recursive (ListNode a) a
type Nat = Recursive NatNode ()
data Recursive w a =
Unit
| Ctor (w (Recursive w a))
?
type List a = Recursive (ListNode a) a
type Nat = Recursive NatNode ()
oneTwo = Ctor (ListNode 1 (Ctor (ListNode 2 Unit)))
two = Ctor (NatNode (Ctor (NatNode Unit)))
data Recursive w a =
Unit
| Ctor (w (Recursive w a))
?
type List a = Recursive (ListNode a) a
type Nat = Recursive NatNode ()
oneTwo = Ctor (ListNode 1 (Ctor (ListNode 2 Unit)))
-- Cons 1 (Cons 2 Unit)
two = Ctor (NatNode (Ctor (NatNode Unit)))
-- Succ (Succ Unit)
data Recursive w a =
Unit
| Ctor (w (Recursive w a))
data Recursive w a =
Unit
| Ctor (w (Recursive w a))
data Recursive w a =
Unit
| Ctor (w (Recursive w a))
Don't we already have a wrapper type that can be unit or a parameterized value?
Don't we already have a wrapper type that can be unit or a parameterized value?
data Maybe a =
Nothing
| Just a
data ListNode a r = ListNode
{ nodeValue :: a
, tailNodes :: Maybe r
}
data NatNode r = NatNode
{ tailNodes :: Maybe r
}
type List a = Recursive (ListNode a)
type Nat = Recursive NatNode
data Recursive w =
Ctor (w (Recursive w))
?
data NatNode r = NatNode
{ tailNodes :: Maybe r
}
type Nat = Recursive NatNode
one :: Nat
one = Ctor (NatNode Nothing)
succ :: Nat -> Nat
succ = Ctor . NatNode . Just
natOne = one
natTwo = succ one
natThree = succ (succ one)
natFour = succ (succ (succ one))
alsoNatFour = succ (natThree)
data Recursive w = Ctor (w (Recursive w))
data ListNode a r = ListNode
{ nodeValue :: a
, tailNodePtr :: Maybe r
}
type List a = Recursive (ListNode a)
empty :: List Int
empty = Ctor (ListNode 10 Nothing)
cons :: a -> List a -> List a
cons a = Ctor . ListNode a . Just
listOne = cons 1 empty
listOneTwo = cons 1 (cons 2 empty)
listOneToThree = cons 1 (cons 2 (cons 3 empty))
listOneToFour = cons 1 (cons 2 (cons 3 (cons 4 empty)))
data Recursive w = Ctor (w (Recursive w))
?
data ListNode a r = ListNode
{ nodeValue :: a
, tailNodePtr :: Maybe r
}
type List a = Recursive (ListNode a)
empty :: List Int
empty = Ctor (ListNode 10 Nothing)
^
cons :: a -> List a -> List a
cons a = Ctor . ListNode a . Just
data Recursive w = Ctor (w (Recursive w))
That's not empty!
What if we flip the wrapper?
data ListNode a r = ListNode
{ nodeValue :: a
, tailNodePtr :: r
}
type List a = Recursive (Maybe (ListNode a))
empty :: List a
empty = Ctor . Nothing
cons :: a -> List a -> List a
cons a = Ctor . ListNode a . Just
listOne = cons 1 empty
listOneTwo = cons 1 (cons 2 empty)
listOneToThree = cons 1 (cons 2 (cons 3 empty))
listOneToFour = cons 1 (cons 2 (cons 3 (cons 4 empty)))
data Recursive w = Ctor (w (Recursive w))
data NatF r =
OneF
| SuccF r
type Nat = Recursive NatF
one :: Nat
one = Ctor OneF
succ :: Nat -> Nat
succ = Ctor . SuccF
natOne = one
natTwo = succ one
natThree = succ (succ one)
natFour = succ (succ (succ one))
alsoNatFour = succ (natThree)
data Recursive w = Ctor (w (Recursive w))
data Natural =
One
| Succ Natural
| = One
/ \
One Succ (|) = Succ One
/ \
One Succ (|) = Succ (Succ One)
/ \
One Succ (|) = Succ (Succ (Succ One))
/ \
One Succ (|) = Succ (Succ (Succ (Succ One)))
/ \
One ... = Succ (Succ (Succ (Succ ...)))
Ctor = Ctor OneF
/ \
OneF SuccF (Ctor) = Ctor (SuccF (Ctor OneF))
/ \
OneF SuccF (Ctor) = Ctor (SuccF (Ctor (SuccF (Ctor OneF
/ \ ))))
OneF SuccF (Ctor) = Ctor (SuccF (Ctor (SuccF (Ctor (SuccF
/ \ (Ctor OneF)))))
OneF SuccF (Ctor) = Ctor (SuccF (Ctor (SuccF (Ctor (SuccF
/ \ (Ctor (SuccF (Ctor OneF))))))
OneF ... = Ctor (SuccF (Ctor (SuccF (Ctor (SuccF
(Ctor (SuccF (Ctor (SuccF
(Ctor OneF)))))))
data Recursive w = Ctor (w (Recursive w))
data NatF r =
OneF
| SuccF r
type Nat = Recursive NatF
one :: Nat
one = Ctor OneF
succ :: Nat -> Nat
succ num = Ctor (SuccF num)
data ListF a r =
EmptyF
| ConsF a r
type List = Recursive (ListF a)
empty :: List
empty = Ctor EmptyF
cons :: a -> List -> List
cons = Ctor . ConsF
listOne = cons 1 empty
listOneTwo = cons 1 (cons 2 empty)
listOneToThree = cons 1 (cons 2 (cons 3 empty))
listOneToFour = cons 1 (cons 2 (cons 3 (cons 4 empty)))
data Recursive w = Ctor (w (Recursive w))
| = Empty
/ \
E Cons
1 (|) = Cons 1 Empty
/ \
E Cons
2 (|) = Cons 1 (Cons 2 Empty)
/ \
E Cons
3 (|) = Cons 1 (Cons 2 (Cons 3 Empty))
/ \
E Cons
4 (|) = Cons 1 (Cons 2 (Cons 3 (Cons 4 Empty)))
/ \
E Cons
5 (...) = Cons 1 (Cons 2 (Cons 3 (Cons 4 (Cons 5 ...))))
data List a =
Empty
| Cons a (List a)
data ListF a r =
EmptyF
| ConsF a r
type List = Recursive (ListF a)
empty :: List
empty = Ctor EmptyF
cons :: a -> List -> List
cons head tail =
Ctor (ConsF head tail)
data Recursive w = Ctor (w (Recursive w))
Ctor = Ctor EmptyF
/ \
EmpF ConsF
1 (Ctor) = Ctor (ConsF 1 (Ctor EmptyF))
/ \
EmpF ConsF
2 (Ctor) = Ctor (ConsF 1 (Ctor (ConsF 2 (Ctor EmptyF
/ \ ))))
EmpF ConsF
3 (Ctor) = Ctor (ConsF 1 (Ctor (ConsF 2 (Ctor (ConsF 3
/ \ (Ctor EmptyF)))))
EmpF ConsF
4 (Ctor) = Ctor (ConsF 1 (Ctor (ConsF 2 (Ctor (ConsF 3
/ \ (Ctor (ConsF 4 (Ctor EmptyF))))))
EmpF ... = Ctor (ConsF 1 (Ctor (ConsF 2 (Ctor (ConsF 3
(Ctor (ConsF 4 (Ctor (ConsF ...
with values at leaves
data Tree
= Leaf Int
| Branch Tree Tree
with values at leaves
data Tree
= Leaf Int
| Branch Tree Tree
someTree :: Tree
someTree =
Branch
(Branch
(Branch (Leaf 1) (Leaf 2))
(Branch (Leaf 3) (Leaf 4))
)
(Leaf 5)
-- •
-- / \
-- • \
-- / \ \
-- • • \
-- / \ / \ \
-- 1 2 3 4 5
someValues = (1,2,3,4,5)
someTree :: Tree
someTree =
Branch
(Branch
(Branch (Leaf 1) (Leaf 2))
(Branch (Leaf 3) (Leaf 4))
)
(Leaf 5)
-- •
-- / \
-- • \
-- / \ \
-- • • \
-- / \ / \ \
-- 1 2 3 4 5
someTree :: Tree
someTree =
Branch
(Branch
(Branch (Leaf 1) (Leaf 2))
(Branch (Leaf 3) (Leaf 4))
)
(Leaf 5)
-- •
-- / \
-- • \
-- / \ \
-- • • \
-- / \ / \ \
-- 1 2 3 4 5
someStructuredValues = (((1, 2), (3, 4)), 5)
someList = (1, 2, 3, 4, 5)
-- •
-- / \
-- • \
-- / \ \
-- • • \
-- / \ / \ \
-- 1 2 3 4 5
-- (,)
-- / \
-- (,) \
-- / \ \
-- (,) (,) \
-- / \ / \ \
-- 1 2 3 4 5
someStructuredValues = (((1, 2), (3, 4)), 5)
-- +
-- / \
-- - \
-- / \ \
-- * + \
-- / \ / \ \
-- 1 2 3 4 5
someExpression = ((1 * 2) - (3 + 4)) + 5
-- Arithmetic Expressions
data Math
= Lit Int
| Add Math Math
| Sub Math Math
| Mul Math Math
someMath :: Math
someMath =
Add
(Sub
(Mul (Lit 1) (Lit 2))
(Add (Lit 3) (Lit 4))
)
(Lit 5)
-- ((1 * 2) - (3 + 4)) + 5
-- +
-- / \
-- - \
-- / \ \
-- * + \
-- / \ / \ \
-- 1 2 3 4 5
data Tree
= Leaf Int
| Branch Tree Tree
data Math
= Lit Int
| Add Math Math
| Sub Math Math
| Mul Math Math
| Neg Math
data Tree a
= Leaf a
| Branch (Tree a) (Tree a)
data Math a
= Lit a
| Add (Math a) (Math a)
| Sub (Math a) (Math a)
| Mul (Math a) (Math a)
| Neg (Math a)
data Tree a
= Leaf a
| Branch (Tree a) (Tree a)
data Math a
= Lit a
| Add (Math a) (Math a)
| Sub (Math a) (Math a)
| Mul (Math a) (Math a)
| Neg (Math a)
Both have a leaf element
data Tree a
= Leaf a
| Branch (Tree a) (Tree a)
data Math a
= Lit a
| Add (Math a) (Math a)
| Sub (Math a) (Math a)
| Mul (Math a) (Math a)
| Neg (Math a)
Both have a branches of
sub-trees/sub-expressions
data Tree a
= Leaf a
| Branch (Pair a)
data Pair a = Pair (Tree a) (Tree a)
data Math a
= Lit a
| Op (Operator a)
data Operator a
= Add (Math a) (Math a)
| Sub (Math a) (Math a)
| Mul (Math a) (Math a)
| Neg (Math a)
data Tree a
= Leaf a
| Branch (Pair (Tree a))
data Pair a = Pair a a
data Math a
= Lit a
| Op (Operator (Math a))
data Operator a
= Add a a
| Sub a a
| Mul a a
| Neg a
data Tree a
= Leaf a
| Branch (Pair (Tree a))
data Pair a = Pair a a
data Math a
= Lit a
| Op (Operator (Math a))
data Operator a
= Add a a
| Sub a a
| Mul a a
| Neg a
data Tree a
= Leaf a
| Branch (Pair (Tree a))
data Pair a = Pair a a
data Math a
= Lit a
| Op (Operator (Math a))
data Operator a
= Add a a
| Sub a a
| Mul a a
| Neg a
type Tree = AST Pair Int
data Pair a = Pair a a
data Math = AST Operator Int
data Operator a
= Add a a
| Sub a a
| Mul a a
| Neg a
data AST op a
= Leaf a
| Branch (op (AST op a))
data Math
= Lit Int
| Add Math Math
| Sub Math Math
| Mul Math Math
someMath :: Math
someMath =
Add
(Sub
(Mul (Lit 1) (Lit 2))
(Add (Lit 3) (Lit 4))
)
(Lit 5)
someAstMath :: Math
someAstMath =
Branch
(Add
(Branch
(Sub
(Branch
(Mul (Leaf 1) (Leaf 2))
)
(Branch
(Add (Leaf 3) (Leaf 4))
)
)
)
(Leaf 5)
)
type Math = AST Operator Int
data Operator a
= Add a a
| Sub a a
| Mul a a
data Math
= Lit Int
| Add Math Math
| Sub Math Math
| Mul Math Math
someMath :: Math
someMath =
Add
(Sub
(Mul (Lit 1) (Lit 2))
(Add (Lit 3) (Lit 4))
)
(Lit 5)
type Math = AST Operator Int
data Operator a
= Add a a
| Sub a a
| Mul a a
someAstMath :: Math
someAstMath =
Branch
(Add
(Branch
(Sub
(Branch
(Mul (Leaf 1) (Leaf 2))
)
(Branch
(Add (Leaf 3) (Leaf 4))
)
)
)
(Leaf 5)
)
An Example
let addOne = (\a -> a + 1)
in addOne 42
addOne = lambda a: a + 1
addOne(42)
Haskell
Python
type SimpleLang = AST Operation Term
newtype Identifier = String
data Operation a
= LetIn Identifier a a -- Variable Binding
| Function Identifier a -- Function Abstraction
| Apply a a -- Apply Two Expressions
data Term
= Var Identifier
| Num Number
let addOne = (\a -> a + 1)
in addOne 42
addOne = lambda a: a + 1
addOne(42)
Haskell
Python
addOne :: SimpleLang
addOne =
(Branch
(LetIn "addOne"
(Branch
(Function "a"
(Apply
(Branch
(Apply
(Leaf (Var "+"))
(Leaf (Var "a"))))
(Leaf (Num 1))))))
(Branch
(Apply
(Leaf (Var "addOne"))
(Leaf (Num 42))))))
data Rose a
with values at all nodes
A rose tree is an n-ary tree
Each node has zero or more children, all of which are themselves rose trees
Unbounded number of branches
Value at every node (including branches)
type List a = [a]
data Rose a
= Leaf a
| Branch (List (Rose a))
[ Branch
[ Branch
[ Branch
[ Leaf 1
, Leaf 2
]
, Branch
[ Leaf 3
, Leaf 4
]
]
, Leaf 5
, Leaf 6
, Leaf 7
]
-- [ ]---
-- / \ \ \
-- [ ] \ \ \
-- / \ \ \ \
-- [ ] [ ] \ \ \
-- / \ / \ \ \ \
-- 1 2 3 4 5 6 7
[ [ [1,2]
, [3,4]
]
, 5
, 6
, 7
]
type List a = [a]
data Rose a
= Leaf a
| Branch (List (Rose a))
[ Branch
[ Branch
[ Branch
[ Leaf 1
, Leaf 2
]
, Branch
[ Leaf 3
, Leaf 4
]
]
, Leaf 5
, Leaf 6
, Leaf 7
]
-- ?----
-- / \ \ \
-- ? \ \ \
-- / \ \ \ \
-- ? ? \ \ \
-- / \ / \ \ \ \
-- 1 2 3 4 5 6 7
type List a = [a]
data Rose a
= Leaf a
| Branch ? (List (Rose a))
[ Branch ?
[ Branch ?
[ Branch ?
[ Leaf 1
, Leaf 2
]
, Branch ?
[ Leaf 3
, Leaf 4
]
]
, Leaf 5
, Leaf 6
, Leaf 7
]
-- ?----
-- [ / \ \ \ ]
-- ? \ \ \
-- [ / \ ] \ \ \
-- ? ? \ \ \
-- [/ \] [/ \] \ \ \
-- 1 2 3 4 5 6 7
( ?
, [( ?
, [ (?, [1,2])
, (?, [3,4])
]
, 5
, 6
, 7
]
)
type List a = [a]
data Rose a
= Leaf a
| Branch a (List (Rose a))
[ Branch 200
[ Branch 300
[ Branch 400
[ Leaf 1
, Leaf 2
]
, Branch 500
[ Leaf 3
, Leaf 4
]
]
, Leaf 5
, Leaf 6
, Leaf 7
]
-- 200---
-- [ / \ \ \ ]
-- 300 \ \ \
-- [ / \ ] \ \ \
-- 400 500 \ \ \
-- [/ \] [/ \] \ \ \
-- 1 2 3 4 5 6 7
( 200
, [ (300
, [(400, [1, 2])
,(500, [3, 4])
]
)
, 5
, 6
, 7
]
)
type List a = [a]
data Rose a
= Leaf a
| Branch a (List (Rose a))
[ Branch 200
[ Branch 300
[ Branch 400
[ Leaf 1
, Leaf 2
]
, Branch 500
[ Leaf 3
, Leaf 4
]
]
, Leaf 5
, Leaf 6
, Leaf 7
]
-- 200---
-- [ / \ \ \ ]
-- 300 \ \ \
-- [ / \ ] \ \ \
-- 400 500 \ \ \
-- [/ \] [/ \] \ \ \
-- 1 2 3 4 5 6 7
type List a = [a]
data Rose a
= Leaf a
| Branch a (List (Rose a))
( 200
, [ (300
, [ (400, [ 1, 2])
, (500, [ 3, 4])
]
)
, 5
, 6
, 7
]
)
[ Branch 200
[ Branch 300
[ Branch 400
[ Leaf 1
, Leaf 2
]
, Branch 500
[ Leaf 3
, Leaf 4
]
]
, Leaf 5
, Leaf 6
, Leaf 7
]
-- 200---
-- [ / \ \ \ ]
-- 300 \ \ \
-- [ / \ ] \ \ \
-- 400 500 \ \ \
-- [/ \] [/ \] \ \ \
-- 1 2 3 4 5 6 7
type List a = [a]
data Rose a
= Branch a (List (Rose a))
( 200
, [ ( 300
, [ (400, [ (1, [])
, (2, [])
]
)
, (500, [ (3, [])
, (4, [])
]
)
]
)
, (5, [])
, (6, [])
, (7, [])
]
)
[ Branch 200
[ Branch 300
[ Branch 400
[ Branch 1 []
, Branch 2 []
]
, Branch 500
[ Branch 3 []
, Branch 4 []
]
]
, Branch 5 []
, Branch 6 []
, Branch 7 []
]
-- 200---
-- [ / \ \ \ ]
-- 300 \ \ \
-- [ / \ ] \ \ \
-- 400 500 \ \ \
-- [/ \] [/ \] \ \ \
-- 1 2 3 4 5 6 7
-- [] [] [] [] [] [] []
-- Annotated
data Annotated f a
= Ann a (f (Annotated f a))
-- equivalent to above
data Annotated' f a = Annotated'
{ annotation :: a
, annSubTree :: f (Annotated' f a)
}
type RoseTree a = Annotated [] a
type Trie a = Annotated (StrMap) a
type List a = Annotated (Identity) a
type DecisionTree a r = Annotated ((->) r) a
type BST a = Annotated (Compose Pair Maybe) a
type MerkleTree a = Annotated (HashTrie) a
Holy Toledo! So Versatile!
data AST f a
= Leaf a
| Branch (f (Tree a))
data Annotated f a
= Annotated a (f (Annotated f a))
-- equivalent to `Annotated`
data Annotated2 f a = Annotated2
{ annotation :: a
, annSubTree :: f (Annotated2 f a)
}
AST
Annotated
-- Free
data Free f a
= Pure a
| Rollup (f (Free a))
-- Cofree
data Cofree f a
= Cofree a (f (Cofree f a))
-- equivalent to `Cofree`
data Cofree2 f a = Cofree2
{ annotation :: a
, annSubTree :: f (Cofree2 f a)
}
Free
Cofree
data Free f a
= Pure a
| Rollup (f (Free a))
data Cofree f a
= a <: (f (Cofree f a))
Free
Cofree
data Recursive w = Ctor (w (Recursive w))
newtype Fix f
= Fix (f (Fix f))
A fixed point (or fixpoint) is a point that, when provided to a function, yields as a result that same point.
newtype Fix f
= Fix (f (Fix f))
data Free f a
= Pure a
| Rollup (f (Free a))
data Cofree f a
= a <: (f (Cofree f a))
freeMath :: Fix MathF
freeMath =
Free
(AddF
(Free
(SubF
(Free
(MulF
(Pure
(1))
(Pure
(2))))
(Free
(AddF
(Pure
(3))
(Pure
(4)))))
(Pure
(5)))
fixList :: Fix ListF
fixList =
Fix
(ConsF 10
(Fix
(ConsF 10
(Fix
(ConsF 10
(Fix
(NilF))))))))
Higher-order Either
Provide either case, and user has to handle both possibilities
data (f :+: g) a
= L1 (f a)
| R1 (g a)
type MbNatural = Fix Maybe
type SumNatural f g = Fix (Identity :+: Proxy)
type List a = Fix ((,) a :+: Proxy)
mbThree = Fix . Just . Fix . Just $ Fix Nothing :: MbNat
sumThree = Fix . L1 . Identity . Fix . L1 . Identity . Fix $ R1 Proxy :: SumNat
oneTwoThree = Fix . L1 . (,) 1 . Fix . L1 . (,) 2
. Fix . L1 . (,) 3 . Fix $ R1 Proxy :: List Int -- ~= [1,2,3]
Higher-order Tuple
Pairs wrappers, and let's user can choose which one they want to use
data (f :*: g) a = f a :*: g a
Blog
A Tour of Some Useful Recursive Types
Video
PS Unscripted - Free From Tree & Halogen VDOM
- Stole for last two exercises