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

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 sheetis 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 afunction 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 wouldlike 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 amore 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 ofthe 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 switchfrom 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 slideis 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