SlideShare a Scribd company logo
CHEAT-SHEET
Folding
#9
∢
/ 
π’‚πŸŽ ∢
/ 
π’‚πŸ ∢
/ 
π’‚πŸ ∢
/ 
π’‚πŸ‘
𝒇
/ 
π’‚πŸŽ 𝒇
/ 
π’‚πŸ 𝒇
/ 
π’‚πŸ 𝒇
/ 
π’‚πŸ‘ 𝒆
List Unfolding
π‘’π‘›π‘“π‘œπ‘™π‘‘ as the Computational Dual of π‘“π‘œπ‘™π‘‘
and how π‘’π‘›π‘“π‘œπ‘™π‘‘ relates to π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’
@philip_schwarz
slides by
https://fpilluminated.org/
This cheat sheet is based on the following deck, which it aims to condense, partly by focusing on a
single running example, and partly by choosing, rather than to provide excerpts from referenced
sources, to simply present salient points made by the sources.
@philip_schwarz
Here is a function called digits which takes an integer number, and returns a list of integers
corresponding to the number’s digits (yes, we are not handling cases like n=0 or negative n).
The digits function makes use of a function called iterate. See the next two slides for the Haskell
and Scala definitions of iterate.
The Haskell version of digits is defined in point-free style, uses the function composition
function, and uses backticks to specify the infix version of functions mod and div. If you find it at
all cryptic, see the slide after the next two for alternative definitions that are less succinct.
digits :: Int -> [Int]
digits =
reverse
. map (`mod` 10)
. takeWhile (/= 0)
. iterate (`div` 10)
def digits(n: Int): List[Int] =
LazyList iterate(n)(_ / 10 )
.takeWhile(_ != 0)
.map( _ % 10 )
.toList
.reverse
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
> digits 2718
[2,7,1,8]
scala> digits(2718)
val res0: List[Int] = List(2, 7, 1, 8)
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
digits :: Int -> [Int]
digits n =
reverse (
map (x -> mod x 10) (
takeWhile (x -> x /= 0) (
iterate (x -> div x 10)
n
)
)
)
digits :: Int -> [Int]
digits n =
reverse $ map (x -> mod x 10)
$ takeWhile (x -> x > 0)
$ iterate (x -> div x 10)
$ n
digits :: Int -> [Int]
digits n =
n & iterate (x -> div x 10)
& takeWhile (x -> x > 0)
& map (x -> mod x 10)
& reverse
digits :: Int -> [Int]
digits =
reverse
. map (x -> mod x 10)
. takeWhile (x -> x > 0)
. iterate (x -> div x 10)
digits :: Int -> [Int]
digits n =
n & iterate (`div` 10)
& takeWhile (/= 0)
& map (`mod` 10)
& reverse
digits :: Int -> [Int]
digits n =
reverse $ map (`mod` 10)
$ takeWhile (/= 0)
$ iterate (`div` 10)
$ n
digits :: Int -> [Int]
digits n =
reverse (
map (`mod` 10) (
takeWhile (/= 0) (
iterate (`div` 10)
n
)
)
)
digits :: Int -> [Int]
digits =
reverse
. map (`mod` 10)
. takeWhile (/= 0)
. iterate (`div` 10)
digits :: Int -> [Int]
digits =
reverse
. map (`mod` 10)
. takeWhile (/= 0)
. iterate (`div` 10)
If you would like to know more about how the digits function works then see
either the following deck, or the one mentioned at the beginning of this deck.
digits :: Int -> [Int]
digits =
reverse
. map (`mod` 10)
. takeWhile (/= 0)
. iterate (`div` 10)
def digits(n: Int): List[Int] =
LazyList iterate(n)(_ / 10)
.takeWhile (_ != 0)
.map (_ % 10)
.toList
.reverse
The digits function composes map, takeWhile and iterate in sequence.
One often finds such a pattern of computation, which may be captured as a generic function called unfold.
π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ (𝛽 β†’ 𝛼) β†’ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘ β„Ž 𝑝 𝑑 = π‘šπ‘Žπ‘ β„Ž ∘ π‘‘π‘Žπ‘˜π‘’π‘€β„Žπ‘–π‘™π‘’ 𝑝 ∘ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑑
unfold :: (b -> a) -> (b -> Bool) -> (b -> b) -> b -> [a]
unfold h p t = map h . takeWhile p . iterate t
def unfold[A,B](h: B => A, p: B => Boolean, t: B => B, b: B): LazyList[A] =
LazyList.iterate(b)(t).takeWhile(p).map(h)
digits :: Int -> [Int]
digits = reverse . unfold (`mod` 10) (/= 0) (`div` 10)
def digits(n: Int): List[Int] =
unfold[Int,Int](_ % 10, _ != 0, _ / 10, n).toList.reverse
digits :: Int -> [Int]
digits =
reverse
. map (`mod` 10)
. takeWhile (/= 0)
. iterate (`div` 10)
def digits(n: Int): List[Int] =
LazyList iterate(n)(_ / 10)
.takeWhile (_ != 0)
.map (_ % 10)
.toList
.reverse
Let’s simplify the implementation
of digits using the unfold function.
π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ (𝛽 β†’ 𝛼) β†’ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘ β„Ž 𝑝 𝑑 = π‘šπ‘Žπ‘ β„Ž ∘ π‘‘π‘Žπ‘˜π‘’π‘€β„Žπ‘–π‘™π‘’ 𝑝 ∘ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑑
Here is a more efficient variant of unfold that fuses together the map, takewhile
and iterate.
The signature is slightly different in that there is some renaming and reordering
of parameters, and the condition tested by predicate function 𝑝 is inverted.
π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’
𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏))
π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ (𝛽 β†’ 𝛼) β†’ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘ β„Ž 𝑝 𝑑 = π‘šπ‘Žπ‘ β„Ž ∘ π‘‘π‘Žπ‘˜π‘’π‘€β„Žπ‘–π‘™π‘’ 𝑝 ∘ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑑
digits :: Int -> [Int]
digits = reverse . unfold (== 0) (`mod` 10) (`div` 10)
digits :: Int -> [Int]
digits = reverse . unfold (`mod` 10) (/= 0) (`div` 10)
def digits(n: Int): List[Int] =
unfold[Int,Int](_ == 0, _ % 10, _ / 10, n).toList.reverse
def digits(n: Int): List[Int] =
unfold[Int,Int](_ % 10, _ != 0, _ / 10, n).toList.reverse
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 = π‘’π‘›π‘“π‘œπ‘™π‘‘ π‘π‘œπ‘›π‘ π‘‘ πΉπ‘Žπ‘™π‘ π‘’ 𝑖𝑑 𝑓
Just for completeness, here is how
iterate can be defined in terms of unfold.
π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’
𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏))
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’
𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏))
The unfold function is not available in Haskell and Scala.
What is available, is an alternative variant which (for reasons that will become apparent later) we shall refer to as unfold’.
In Haskell, unfold’ is called unfoldr (a right unfold). In Scala it is called unfold.
Let’s reimplement digits using the unfold’ function.
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟
π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’
𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣)
unfoldr unfold
digits :: Int -> [Int]
digits = reverse . unfoldr gen
where gen 0 = Nothing
gen d = Just (d `mod` 10, d `div` 10)
def digits(n: Int): List[Int] =
LazyList.unfold(n):
case 0 => None
case x => Some(x % 10, x / 10)
.toList.reverse
digits :: Int -> [Int]
digits = reverse . unfold (== 0) (`mod` 10) (`div` 10)
def digits(n: Int): List[Int] =
unfold[Int,Int](_ == 0, _ % 10, _ / 10, n).toList.reverse
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 = π‘’π‘›π‘“π‘œπ‘™π‘‘β€² πœ†π‘₯. 𝑱𝒖𝒔𝒕 (π‘₯, 𝑓 π‘₯)
Just for completeness, here is how iterate
can be defined in terms of unfold’.
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼
π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟
π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’
𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣)
unfoldr unfold
The dual of the digits function is the decimal function, which given a list of
digits, returns the integer number with such digits.
As seen below, the decimal function is neatly and efficiently implemented
using a left fold (if you want to know more then see folding cheat-sheet #4).
decimal :: [Int] -> Int
decimal = foldl (βŠ•) 0
(βŠ•) :: Int -> Int -> Int
n βŠ• d = 10 * n + d
def decimal(ds: List[Int]): Int =
ds.foldLeft(0)(_βŠ•_)
extension (n: Int)
def βŠ•(d Int): Int = 10 * n + d
> decimal(List(2,7,1,8))
val res0: Int = 2718
> decimal [2,7,1,8]
2718
Given the following…
β€’ decimal is the computational dual of digits
β€’ decimal can be implemented using unfold’ (a right unfold)
β€’ the computational dual of unfold’ is fold’ (a right fold)
…let’s see if decimal can be implemented using fold’.
Because fold’ is not available in Haskell and Scala, we implement it ourselves.
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟
π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’
𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣)
π‘“π‘œπ‘™π‘‘β€² ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽
π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘΅π’Šπ’ = 𝑓 π‘΅π’π’•π’‰π’Šπ’π’ˆ
π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 (𝑱𝒖𝒔𝒕 π‘₯, π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘₯𝑠)
unfoldr unfold
dual
dual
digits :: Int -> [Int]
digits = reverse . unfoldr gen
where gen 0 = Nothing
gen d = Just (d `mod` 10, d `div` 10)
def digits(n: Int): List[Int] =
LazyList.unfold(n):
case 0 => None
case x => Some(x % 10, x / 10)
.toList.reverse
dual
fold' :: (Maybe (a, b) -> b) -> [a] -> b
fold' f [] = f Nothing
fold' f (x:xs) = f (Just (x, fold' f xs))
decimal' :: [Int] -> Int
decimal' = fst . fold' f
where f Nothing = (0, 0)
f (Just (d, (n,e))) = (d * (10 ^ e) + n, e + 1)
def decimalp(ds: List[Int]): Int =
foldp[Int,(Int,Int)](ds){
case None => (0,0)
case Some(d, (n, e)) => (d * (pow(10, e)).toInt + n, e + 1)
}(0)
def foldp[A,B](as: List[A])(f: Option[(A,B)] => B): B = as match
case Nil => f(None)
case x :: xs => f(Option(x, foldp(xs)(f)))
decimal digits
duals
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² π‘“π‘œπ‘™π‘‘β€²
duals
uses
uses
Let’s now switch from fold’ to the more customary
right fold, which is provided by Haskell and Scala.
π‘“π‘œπ‘™π‘‘β€² ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽
π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘΅π’Šπ’ = 𝑓 π‘΅π’π’•π’‰π’Šπ’π’ˆ
π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 (𝑱𝒖𝒔𝒕 π‘₯, π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘₯𝑠)
π‘“π‘œπ‘™π‘‘ ∷ 𝛼 β†’ 𝛽 β†’ 𝛽 β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽
π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘΅π’Šπ’ = 𝑒
π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 π‘₯ π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘₯𝑠
foldr foldRight
decimal :: [Int] -> Int
decimal = fst . foldr f (0, 0)
where f d (n, e) = (d * (10 ^ e) + n, e + 1)
decimal' :: [Int] -> Int
decimal' = fst . fold' f
where f Nothing = (0, 0)
f (Just (d, (n,e))) = (d * (10 ^ e) + n, e + 1)
def decimalp(ds: List[Int]): Int =
foldp[Int,(Int,Int)](ds){
case None => (0,0)
case Some(d, (n, e)) => (d * (pow(10, e)).toInt + n, e + 1)
}(0)
def decimal(ds: List[Int]): Int =
ds.foldRight(0,0){ case (d, (n, e)) =>
(d * (pow(10, e)).toInt + n, e + 1)
}(0)
The next slide is the penultimate one and suggests that
β€’ the introduction of fold’ and unfold’ makes the duality between folding and unfolding very clear
β€’ the names of fold’ and unfold’ are primed because the two functions are less convenient for programming than fold and unfold.
The slide after that is the last one and introduces the terms catamorphism and anamorphism.
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟
π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’
𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣)
π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’
𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏))
π‘“π‘œπ‘™π‘‘ ∷ 𝛼 β†’ 𝛽 β†’ 𝛽 β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽
π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘΅π’Šπ’ = 𝑒
π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 π‘₯ π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘₯𝑠
π‘“π‘œπ‘™π‘‘β€² ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽
π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘΅π’Šπ’ = 𝑓 π‘΅π’π’•π’‰π’Šπ’π’ˆ
π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 (𝑱𝒖𝒔𝒕 π‘₯, π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘₯𝑠)
π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽)
𝛽 π‘³π’Šπ’”π’• 𝛼
𝑓
π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓
π‘³π’Šπ’”π’• 𝛼
π‘“π‘œπ‘™π‘‘β€² 𝑓
𝛽
π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽)
𝑓
While they may sometimes be less convenient for programming with, π‘“π‘œπ‘™π‘‘β€² and π‘’π‘›π‘“π‘œπ‘™π‘‘β€², the primed
versions of π‘“π‘œπ‘™π‘‘ and π‘’π‘›π‘“π‘œπ‘™π‘‘, make the duality between the fold and the unfold very clear
The two diagrams are based on ones in Conal Elliott’s deck Folds and Unfolds all around us: http://conal.net/talks/folds-and-unfolds.pdf
foldr foldRight
unfoldr unfold
π‘’π‘›π‘“π‘œπ‘™π‘‘πΏ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘“π‘œπ‘™π‘‘πΏ ∷ 𝛼 β†’ 𝛽 β†’ 𝛽 β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽
Functions that β€˜destruct’ a list – they have been
called catamorphisms, from the Greek proposition
κατά, meaning β€˜downwards’ as in β€˜catastrophe’.
Functions that β€˜generate’ a list of items of type 𝛼 from a seed
of type 𝛽 – they have been called anamorphisms, from the
Greek proposition αΌ€Ξ½Ξ¬, meaning β€˜upwards’ as in β€˜anabolism’.
π‘’π‘›π‘“π‘œπ‘™π‘‘πΏ& ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼
π‘“π‘œπ‘™π‘‘πΏ& ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽
unfoldr unfold
Based on slightly modified versions of two sentences from the paper Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire
Available here https://maartenfokkinga.github.io/utwente/mmf91m.pdf
foldr foldRight

More Related Content

PDF
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
PDF
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
PDF
Folding Cheat Sheet #4 - fourth in a series
PDF
Left and Right Folds - Comparison of a mathematical definition and a programm...
PDF
Folding Cheat Sheet #7 - seventh in a series
PDF
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
PDF
{- Do not change the skeleton code! The point of this assign.pdf
PDF
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala Part 2 ...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
Folding Cheat Sheet #4 - fourth in a series
Left and Right Folds - Comparison of a mathematical definition and a programm...
Folding Cheat Sheet #7 - seventh in a series
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
{- Do not change the skeleton code! The point of this assign.pdf
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala Part 2 ...

Similar to Folding Cheat Sheet # 9 - List Unfolding π‘’π‘›π‘“π‘œπ‘™π‘‘ as the Computational Dual of π‘“π‘œπ‘™π‘‘ and how π‘’π‘›π‘“π‘œπ‘™π‘‘ relates to π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ (20)

PDF
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - Part 2
PDF
Scala by Luc Duponcheel
PDF
Scala Functional Patterns
Β 
PDF
Fibonacci Function Gallery - Part 2 - One in a series
PDF
Fp in scala part 1
KEY
An Introduction to Functional Programming using Haskell
PPT
Pythonic Math
PPTX
Python Tidbits
ODP
Python quickstart for programmers: Python Kung Fu
PDF
Introduction Γ  Scala - Michel Schinz - January 2010
PDF
A Few of My Favorite (Python) Things
PDF
Folding Cheat Sheet #3 - third in a series
PDF
Introduction to Functional Languages
Β 
ODP
Scala as a Declarative Language
PDF
Introduction to parallel and distributed computation with spark
PDF
Python collections
PDF
Python slide
PDF
python.pdf
PDF
Sequence and Traverse - Part 1
PPTX
Data Structures and Algorithms in Python
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - Part 2
Scala by Luc Duponcheel
Scala Functional Patterns
Β 
Fibonacci Function Gallery - Part 2 - One in a series
Fp in scala part 1
An Introduction to Functional Programming using Haskell
Pythonic Math
Python Tidbits
Python quickstart for programmers: Python Kung Fu
Introduction Γ  Scala - Michel Schinz - January 2010
A Few of My Favorite (Python) Things
Folding Cheat Sheet #3 - third in a series
Introduction to Functional Languages
Β 
Scala as a Declarative Language
Introduction to parallel and distributed computation with spark
Python collections
Python slide
python.pdf
Sequence and Traverse - Part 1
Data Structures and Algorithms in Python
Ad

More from Philip Schwarz (20)

PDF
ApplicativeError functions handling and recovering from errors: A mnemonic to...
PDF
Folding Cheat Sheet Series Titles - a series of 9 decks
PDF
Drawing Heighway’s Dragon - Part 4 - Interactive and Animated Dragon Creation
PDF
The Nature of Complexity in John Ousterhout’s Philosophy of Software Design
PDF
Drawing Heighway’s Dragon - Part 3 - Simplification Through Separation of Con...
PDF
The Open-Closed Principle - Part 2 - The Contemporary Version - An Introduction
PDF
The Open-Closed Principle - Part 1 - The Original Version
PDF
Drawing Heighway’s Dragon - Part II - Recursive Function Simplification - Fro...
PDF
Drawing Heighway’s Dragon - Recursive Function Rewrite - From Imperative Styl...
PDF
Fibonacci Function Gallery - Part 1 (of a series) - with minor corrections
PDF
Fibonacci Function Gallery - Part 1 (of a series)
PDF
The Debt Metaphor - Ward Cunningham in his 2009 YouTube video
PDF
Folding Cheat Sheet Series Titles (so far)
PDF
From Subtype Polymorphism To Typeclass-based Ad hoc Polymorphism - An Example
PDF
Folding Cheat Sheet #8 - eighth in a series
PDF
Function Applicative for Great Good of Leap Year Function
PDF
Folding Cheat Sheet #6 - sixth in a series
PDF
Folding Cheat Sheet #5 - fifth in a series
PDF
Hand Rolled Applicative User Validation Code Kata
PDF
A Sighting of filterA in Typelevel Rite of Passage
ApplicativeError functions handling and recovering from errors: A mnemonic to...
Folding Cheat Sheet Series Titles - a series of 9 decks
Drawing Heighway’s Dragon - Part 4 - Interactive and Animated Dragon Creation
The Nature of Complexity in John Ousterhout’s Philosophy of Software Design
Drawing Heighway’s Dragon - Part 3 - Simplification Through Separation of Con...
The Open-Closed Principle - Part 2 - The Contemporary Version - An Introduction
The Open-Closed Principle - Part 1 - The Original Version
Drawing Heighway’s Dragon - Part II - Recursive Function Simplification - Fro...
Drawing Heighway’s Dragon - Recursive Function Rewrite - From Imperative Styl...
Fibonacci Function Gallery - Part 1 (of a series) - with minor corrections
Fibonacci Function Gallery - Part 1 (of a series)
The Debt Metaphor - Ward Cunningham in his 2009 YouTube video
Folding Cheat Sheet Series Titles (so far)
From Subtype Polymorphism To Typeclass-based Ad hoc Polymorphism - An Example
Folding Cheat Sheet #8 - eighth in a series
Function Applicative for Great Good of Leap Year Function
Folding Cheat Sheet #6 - sixth in a series
Folding Cheat Sheet #5 - fifth in a series
Hand Rolled Applicative User Validation Code Kata
A Sighting of filterA in Typelevel Rite of Passage
Ad

Recently uploaded (20)

PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards β€” Omne...
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Β 
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Softaken Excel to vCard Converter Software.pdf
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PPTX
Essential Infomation Tech presentation.pptx
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PPTX
ai tools demonstartion for schools and inter college
PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PDF
Nekopoi APK 2025 free lastest update
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Odoo Companies in India – Driving Business Transformation.pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards β€” Omne...
2025 Textile ERP Trends: SAP, Odoo & Oracle
VVF-Customer-Presentation2025-Ver1.9.pptx
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Β 
How to Migrate SBCGlobal Email to Yahoo Easily
How to Choose the Right IT Partner for Your Business in Malaysia
Softaken Excel to vCard Converter Software.pdf
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Essential Infomation Tech presentation.pptx
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
ai tools demonstartion for schools and inter college
Reimagine Home Health with the Power of Agentic AI​
wealthsignaloriginal-com-DS-text-... (1).pdf
Nekopoi APK 2025 free lastest update

Folding Cheat Sheet # 9 - List Unfolding π‘’π‘›π‘“π‘œπ‘™π‘‘ as the Computational Dual of π‘“π‘œπ‘™π‘‘ and how π‘’π‘›π‘“π‘œπ‘™π‘‘ relates to π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’

  • 1. CHEAT-SHEET Folding #9 ∢ / π’‚πŸŽ ∢ / π’‚πŸ ∢ / π’‚πŸ ∢ / π’‚πŸ‘ 𝒇 / π’‚πŸŽ 𝒇 / π’‚πŸ 𝒇 / π’‚πŸ 𝒇 / π’‚πŸ‘ 𝒆 List Unfolding π‘’π‘›π‘“π‘œπ‘™π‘‘ as the Computational Dual of π‘“π‘œπ‘™π‘‘ and how π‘’π‘›π‘“π‘œπ‘™π‘‘ relates to π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ @philip_schwarz slides by https://fpilluminated.org/
  • 2. This cheat sheet is based on the following deck, which it aims to condense, partly by focusing on a single running example, and partly by choosing, rather than to provide excerpts from referenced sources, to simply present salient points made by the sources. @philip_schwarz
  • 3. Here is a function called digits which takes an integer number, and returns a list of integers corresponding to the number’s digits (yes, we are not handling cases like n=0 or negative n). The digits function makes use of a function called iterate. See the next two slides for the Haskell and Scala definitions of iterate. The Haskell version of digits is defined in point-free style, uses the function composition function, and uses backticks to specify the infix version of functions mod and div. If you find it at all cryptic, see the slide after the next two for alternative definitions that are less succinct. digits :: Int -> [Int] digits = reverse . map (`mod` 10) . takeWhile (/= 0) . iterate (`div` 10) def digits(n: Int): List[Int] = LazyList iterate(n)(_ / 10 ) .takeWhile(_ != 0) .map( _ % 10 ) .toList .reverse π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼 π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯) > digits 2718 [2,7,1,8] scala> digits(2718) val res0: List[Int] = List(2, 7, 1, 8)
  • 4. π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼 π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
  • 5. π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼 π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
  • 6. digits :: Int -> [Int] digits n = reverse ( map (x -> mod x 10) ( takeWhile (x -> x /= 0) ( iterate (x -> div x 10) n ) ) ) digits :: Int -> [Int] digits n = reverse $ map (x -> mod x 10) $ takeWhile (x -> x > 0) $ iterate (x -> div x 10) $ n digits :: Int -> [Int] digits n = n & iterate (x -> div x 10) & takeWhile (x -> x > 0) & map (x -> mod x 10) & reverse digits :: Int -> [Int] digits = reverse . map (x -> mod x 10) . takeWhile (x -> x > 0) . iterate (x -> div x 10) digits :: Int -> [Int] digits n = n & iterate (`div` 10) & takeWhile (/= 0) & map (`mod` 10) & reverse digits :: Int -> [Int] digits n = reverse $ map (`mod` 10) $ takeWhile (/= 0) $ iterate (`div` 10) $ n digits :: Int -> [Int] digits n = reverse ( map (`mod` 10) ( takeWhile (/= 0) ( iterate (`div` 10) n ) ) ) digits :: Int -> [Int] digits = reverse . map (`mod` 10) . takeWhile (/= 0) . iterate (`div` 10) digits :: Int -> [Int] digits = reverse . map (`mod` 10) . takeWhile (/= 0) . iterate (`div` 10)
  • 7. If you would like to know more about how the digits function works then see either the following deck, or the one mentioned at the beginning of this deck.
  • 8. digits :: Int -> [Int] digits = reverse . map (`mod` 10) . takeWhile (/= 0) . iterate (`div` 10) def digits(n: Int): List[Int] = LazyList iterate(n)(_ / 10) .takeWhile (_ != 0) .map (_ % 10) .toList .reverse The digits function composes map, takeWhile and iterate in sequence. One often finds such a pattern of computation, which may be captured as a generic function called unfold. π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ (𝛽 β†’ 𝛼) β†’ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘ β„Ž 𝑝 𝑑 = π‘šπ‘Žπ‘ β„Ž ∘ π‘‘π‘Žπ‘˜π‘’π‘€β„Žπ‘–π‘™π‘’ 𝑝 ∘ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑑 unfold :: (b -> a) -> (b -> Bool) -> (b -> b) -> b -> [a] unfold h p t = map h . takeWhile p . iterate t def unfold[A,B](h: B => A, p: B => Boolean, t: B => B, b: B): LazyList[A] = LazyList.iterate(b)(t).takeWhile(p).map(h)
  • 9. digits :: Int -> [Int] digits = reverse . unfold (`mod` 10) (/= 0) (`div` 10) def digits(n: Int): List[Int] = unfold[Int,Int](_ % 10, _ != 0, _ / 10, n).toList.reverse digits :: Int -> [Int] digits = reverse . map (`mod` 10) . takeWhile (/= 0) . iterate (`div` 10) def digits(n: Int): List[Int] = LazyList iterate(n)(_ / 10) .takeWhile (_ != 0) .map (_ % 10) .toList .reverse Let’s simplify the implementation of digits using the unfold function. π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ (𝛽 β†’ 𝛼) β†’ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘ β„Ž 𝑝 𝑑 = π‘šπ‘Žπ‘ β„Ž ∘ π‘‘π‘Žπ‘˜π‘’π‘€β„Žπ‘–π‘™π‘’ 𝑝 ∘ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑑
  • 10. Here is a more efficient variant of unfold that fuses together the map, takewhile and iterate. The signature is slightly different in that there is some renaming and reordering of parameters, and the condition tested by predicate function 𝑝 is inverted. π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’ 𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏)) π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ (𝛽 β†’ 𝛼) β†’ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘ β„Ž 𝑝 𝑑 = π‘šπ‘Žπ‘ β„Ž ∘ π‘‘π‘Žπ‘˜π‘’π‘€β„Žπ‘–π‘™π‘’ 𝑝 ∘ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑑 digits :: Int -> [Int] digits = reverse . unfold (== 0) (`mod` 10) (`div` 10) digits :: Int -> [Int] digits = reverse . unfold (`mod` 10) (/= 0) (`div` 10) def digits(n: Int): List[Int] = unfold[Int,Int](_ == 0, _ % 10, _ / 10, n).toList.reverse def digits(n: Int): List[Int] = unfold[Int,Int](_ % 10, _ != 0, _ / 10, n).toList.reverse
  • 11. π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼 π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 = π‘’π‘›π‘“π‘œπ‘™π‘‘ π‘π‘œπ‘›π‘ π‘‘ πΉπ‘Žπ‘™π‘ π‘’ 𝑖𝑑 𝑓 Just for completeness, here is how iterate can be defined in terms of unfold. π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’ 𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏)) π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼 π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯)
  • 12. π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’ 𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏)) The unfold function is not available in Haskell and Scala. What is available, is an alternative variant which (for reasons that will become apparent later) we shall refer to as unfold’. In Haskell, unfold’ is called unfoldr (a right unfold). In Scala it is called unfold. Let’s reimplement digits using the unfold’ function. π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟 π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’ 𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣) unfoldr unfold digits :: Int -> [Int] digits = reverse . unfoldr gen where gen 0 = Nothing gen d = Just (d `mod` 10, d `div` 10) def digits(n: Int): List[Int] = LazyList.unfold(n): case 0 => None case x => Some(x % 10, x / 10) .toList.reverse digits :: Int -> [Int] digits = reverse . unfold (== 0) (`mod` 10) (`div` 10) def digits(n: Int): List[Int] = unfold[Int,Int](_ == 0, _ % 10, _ / 10, n).toList.reverse
  • 13. π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼 π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 = π‘’π‘›π‘“π‘œπ‘™π‘‘β€² πœ†π‘₯. 𝑱𝒖𝒔𝒕 (π‘₯, 𝑓 π‘₯) Just for completeness, here is how iterate can be defined in terms of unfold’. π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ ∷ 𝛼 β†’ 𝛼 β†’ 𝛼 β†’ 𝛼 π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 π‘₯ = π‘₯ ∢ π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘‘π‘’ 𝑓 (𝑓 π‘₯) π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟 π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’ 𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣) unfoldr unfold
  • 14. The dual of the digits function is the decimal function, which given a list of digits, returns the integer number with such digits. As seen below, the decimal function is neatly and efficiently implemented using a left fold (if you want to know more then see folding cheat-sheet #4). decimal :: [Int] -> Int decimal = foldl (βŠ•) 0 (βŠ•) :: Int -> Int -> Int n βŠ• d = 10 * n + d def decimal(ds: List[Int]): Int = ds.foldLeft(0)(_βŠ•_) extension (n: Int) def βŠ•(d Int): Int = 10 * n + d > decimal(List(2,7,1,8)) val res0: Int = 2718 > decimal [2,7,1,8] 2718
  • 15. Given the following… β€’ decimal is the computational dual of digits β€’ decimal can be implemented using unfold’ (a right unfold) β€’ the computational dual of unfold’ is fold’ (a right fold) …let’s see if decimal can be implemented using fold’. Because fold’ is not available in Haskell and Scala, we implement it ourselves. π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟 π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’ 𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣) π‘“π‘œπ‘™π‘‘β€² ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽 π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘΅π’Šπ’ = 𝑓 π‘΅π’π’•π’‰π’Šπ’π’ˆ π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 (𝑱𝒖𝒔𝒕 π‘₯, π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘₯𝑠) unfoldr unfold dual dual digits :: Int -> [Int] digits = reverse . unfoldr gen where gen 0 = Nothing gen d = Just (d `mod` 10, d `div` 10) def digits(n: Int): List[Int] = LazyList.unfold(n): case 0 => None case x => Some(x % 10, x / 10) .toList.reverse dual fold' :: (Maybe (a, b) -> b) -> [a] -> b fold' f [] = f Nothing fold' f (x:xs) = f (Just (x, fold' f xs)) decimal' :: [Int] -> Int decimal' = fst . fold' f where f Nothing = (0, 0) f (Just (d, (n,e))) = (d * (10 ^ e) + n, e + 1) def decimalp(ds: List[Int]): Int = foldp[Int,(Int,Int)](ds){ case None => (0,0) case Some(d, (n, e)) => (d * (pow(10, e)).toInt + n, e + 1) }(0) def foldp[A,B](as: List[A])(f: Option[(A,B)] => B): B = as match case Nil => f(None) case x :: xs => f(Option(x, foldp(xs)(f))) decimal digits duals π‘’π‘›π‘“π‘œπ‘™π‘‘β€² π‘“π‘œπ‘™π‘‘β€² duals uses uses
  • 16. Let’s now switch from fold’ to the more customary right fold, which is provided by Haskell and Scala. π‘“π‘œπ‘™π‘‘β€² ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽 π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘΅π’Šπ’ = 𝑓 π‘΅π’π’•π’‰π’Šπ’π’ˆ π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 (𝑱𝒖𝒔𝒕 π‘₯, π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘₯𝑠) π‘“π‘œπ‘™π‘‘ ∷ 𝛼 β†’ 𝛽 β†’ 𝛽 β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽 π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘΅π’Šπ’ = 𝑒 π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 π‘₯ π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘₯𝑠 foldr foldRight decimal :: [Int] -> Int decimal = fst . foldr f (0, 0) where f d (n, e) = (d * (10 ^ e) + n, e + 1) decimal' :: [Int] -> Int decimal' = fst . fold' f where f Nothing = (0, 0) f (Just (d, (n,e))) = (d * (10 ^ e) + n, e + 1) def decimalp(ds: List[Int]): Int = foldp[Int,(Int,Int)](ds){ case None => (0,0) case Some(d, (n, e)) => (d * (pow(10, e)).toInt + n, e + 1) }(0) def decimal(ds: List[Int]): Int = ds.foldRight(0,0){ case (d, (n, e)) => (d * (pow(10, e)).toInt + n, e + 1) }(0)
  • 17. The next slide is the penultimate one and suggests that β€’ the introduction of fold’ and unfold’ makes the duality between folding and unfolding very clear β€’ the names of fold’ and unfold’ are primed because the two functions are less convenient for programming than fold and unfold. The slide after that is the last one and introduces the terms catamorphism and anamorphism.
  • 18. π‘’π‘›π‘“π‘œπ‘™π‘‘β€² ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑒 = 𝐜𝐚𝐬𝐞 𝑓 𝑒 𝐨𝐟 π‘΅π’π’•π’‰π’Šπ’π’ˆ β†’ π‘΅π’Šπ’ 𝑱𝒖𝒔𝒕 (π‘₯, 𝑣) β†’ π‘ͺ𝒐𝒏𝒔 π‘₯ (π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 𝑣) π‘’π‘›π‘“π‘œπ‘™π‘‘ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 𝑏 = 𝐒𝐟 𝑝 𝑏 𝐭𝐑𝐞𝐧 π‘΅π’Šπ’ 𝐞π₯𝐬𝐞 π‘ͺ𝒐𝒏𝒔 (𝑓 𝑏) (π‘’π‘›π‘“π‘œπ‘™π‘‘ 𝑝 𝑓 𝑔 (𝑔 𝑏)) π‘“π‘œπ‘™π‘‘ ∷ 𝛼 β†’ 𝛽 β†’ 𝛽 β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽 π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘΅π’Šπ’ = 𝑒 π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 π‘₯ π‘“π‘œπ‘™π‘‘ 𝑓 𝑒 π‘₯𝑠 π‘“π‘œπ‘™π‘‘β€² ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽 π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘΅π’Šπ’ = 𝑓 π‘΅π’π’•π’‰π’Šπ’π’ˆ π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘ͺ𝒐𝒏𝒔 π‘₯ π‘₯𝑠 = 𝑓 (𝑱𝒖𝒔𝒕 π‘₯, π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘₯𝑠) π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) 𝛽 π‘³π’Šπ’”π’• 𝛼 𝑓 π‘’π‘›π‘“π‘œπ‘™π‘‘β€² 𝑓 π‘³π’Šπ’”π’• 𝛼 π‘“π‘œπ‘™π‘‘β€² 𝑓 𝛽 π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) 𝑓 While they may sometimes be less convenient for programming with, π‘“π‘œπ‘™π‘‘β€² and π‘’π‘›π‘“π‘œπ‘™π‘‘β€², the primed versions of π‘“π‘œπ‘™π‘‘ and π‘’π‘›π‘“π‘œπ‘™π‘‘, make the duality between the fold and the unfold very clear The two diagrams are based on ones in Conal Elliott’s deck Folds and Unfolds all around us: http://conal.net/talks/folds-and-unfolds.pdf foldr foldRight unfoldr unfold
  • 19. π‘’π‘›π‘“π‘œπ‘™π‘‘πΏ ∷ 𝛽 β†’ 𝑩𝒐𝒐𝒍 β†’ (𝛽 β†’ 𝛼) β†’ (𝛽 β†’ 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘“π‘œπ‘™π‘‘πΏ ∷ 𝛼 β†’ 𝛽 β†’ 𝛽 β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽 Functions that β€˜destruct’ a list – they have been called catamorphisms, from the Greek proposition κατά, meaning β€˜downwards’ as in β€˜catastrophe’. Functions that β€˜generate’ a list of items of type 𝛼 from a seed of type 𝛽 – they have been called anamorphisms, from the Greek proposition αΌ€Ξ½Ξ¬, meaning β€˜upwards’ as in β€˜anabolism’. π‘’π‘›π‘“π‘œπ‘™π‘‘πΏ& ∷ 𝛽 β†’ π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽 β†’ π‘³π’Šπ’”π’• 𝛼 π‘“π‘œπ‘™π‘‘πΏ& ∷ (π‘΄π’‚π’šπ’ƒπ’† (𝛼, 𝛽) β†’ 𝛽) β†’ π‘³π’Šπ’”π’• 𝛼 β†’ 𝛽 unfoldr unfold Based on slightly modified versions of two sentences from the paper Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire Available here https://maartenfokkinga.github.io/utwente/mmf91m.pdf foldr foldRight