Recursion for the Masses TCS Seminar WS19/20 Christoph Rauch Dec - - PowerPoint PPT Presentation

recursion for the masses
SMART_READER_LITE
LIVE PREVIEW

Recursion for the Masses TCS Seminar WS19/20 Christoph Rauch Dec - - PowerPoint PPT Presentation

Recursion for the Masses Recursion for the Masses TCS Seminar WS19/20 Christoph Rauch Dec 10, 2019 Recursion for the Masses Introduction of Recursion to Students Early Stage (FAU) students are more or less expected to "know" what


slide-1
SLIDE 1

Recursion for the Masses

Recursion for the Masses

TCS Seminar WS19/20 Christoph Rauch Dec 10, 2019

slide-2
SLIDE 2

Recursion for the Masses

Introduction of Recursion to Students

Early Stage (FAU)

students are more or less expected to "know" what recursion is no definition at all in maths classes "Java can do it" plus a definition of what a recursive definition looks like in Java in GDA (now AuD) tail recursion, mutual recursion, divide & conquer similarly "Haskell can do it" in PFP literally recommended to use iteration whenever possible nowadays, students are blessed with ThProg!

slide-3
SLIDE 3

Recursion for the Masses

First Real Contact

Theory of Programming

We learn that . . .

(inductive) data types are initial algebras to functors initiality provides unique solutions to recursive equations of a certain form; the "recursion scheme" of folds (and, in fact, the more general primitive recursion) there’s a dual concept (namely final coalgebras) for coinductive data

first examples of structured recursion not the end of the line!

slide-4
SLIDE 4

Recursion for the Masses

Recursion Schemes

coElgot algebra

… may short-circuit while tearing a ⨯ g b → b; ana

Elgot algebra

… may short-circuit while building cata; a → b ∨ f a

exomorphism

???

synchromorphism

???

dynamorphism

histo; ana

chronomorphism

histo; futu

hylomorphism

cata; ana ↔

metamorphism

ana; cata folds (tear down a structure) algebra f a → Fix f → a unfolds (build up a structure) coalgebra f a → a → Fix f

catamorphism

f a → a

anamorphism

a → f a

paramorphism*

… with primitive recursion f (Fix f ⨯ a) → a

apomorphism*

… returning a branch or single level a → f (Fix f ∨ a)

zygomorphism*

… with a helper function (f b → b) → (f (b ⨯ a) → a)

histomorphism

… with prev. answers it has given f (Cofree f a) → a

prepromorphism*

… after applying a NatTrans (f a → a) → (f ↝ f)

futumorphism

… multiple levels at a time a → f (Free f a)

postpromorphism*

… before applying a NatTrans (a → f a) → (f ↝ f) refolds (build up then tear down a structure) algebra g b → (f ↝ g) → coalgebra f a → a → b reunfolds (tear down then build up a structure) coalgebra g b → (a → b) → algebra f a → Fix f → Fix g

Recursion Schemes

codynamorphism

cata; futu

Stolen from Edward Kmett’s http://comonad.com/reader/ 2009/recursion-schemes/ * This gives rise to a family of related recursion schemes, modeled in recursion-schemes with distributive law combinators

g apomorphism

(b → f b) → (a → f (b ∨ a)) These can be combined in various ways. For example, a “zygohistomorphic prepromorphism” combines the zygo, histo, and prepro aspects into a signature like (f b → b) → (f ↝ f) → (f (w (b ⨯ a)) → a) → Fix f → a

generalized

(f w ↝ w f) → (f (w α) → β)

g futumorphism

(h f ↝ f h) → (a → f (m a))

g histomorphism

(f h ↝ h f) → (f (w a) → a)

generalized

(m f ↝ f m) → (α → f (m β))

generalized

apply the generalizations for both the relevant fold and unfold

generalized

apply … both … [un]fold

  • thers

mutumorphism

… can refer to each other’s results (f (a ⨯ b) → a) → (f (a ⨯ b) → b)

slide-5
SLIDE 5

Recursion for the Masses

Recursion Schemes

Ban General Recursion

using general recursion carelessly = chaos (structured programming : goto) as (recursion schemes : general recursion) [Meijer et al. 2006]

Use Schemes Instead

enjoy many desirable properties (e.g. fusion, compositionality) expressive, generic, concise

slide-6
SLIDE 6

Recursion for the Masses

Recursion Schemes

Ban General Recursion

using general recursion carelessly = chaos (structured programming : goto) as (recursion schemes : general recursion) [Meijer et al. 2006]

Use Schemes Instead

enjoy many desirable properties (e.g. fusion, compositionality) expressive, generic, concise a little bit scary but knowing about them can help make code more efficient and safe

slide-7
SLIDE 7

Recursion for the Masses

Example: Zygomorphism I

Task:

write function with property f [v,w,x,y,z] = v - (w + (x - (y + z))) first attempt: lengthEven :: [a] -> Bool lengthEven = even . length f [] = 0 f (x:xs) = if lengthEven xs then x - f xs else x + f xs

slide-8
SLIDE 8

Recursion for the Masses

Example: Zygomorphism II

First,

notice that the rhs of f needs access to f xs as well as x and xs there is a scheme for that! paramorphisms – just a fancy name for primitive recursion on inductive data types: para :: (a -> [a] -> b -> b) -> b -> [a] -> b para f z [] = z para f z (x:xs) = f x xs (para f z xs) f = para (\x xs ac -> if lengthEven xs then x - ac else x + ac) 0

slide-9
SLIDE 9

Recursion for the Masses

Example: Zygomorphism III

Now . . .

lengthEven is called in each recursive call quadratic algorithm but: both lengthEven and para are catamorphisms (folds): lengthEven = cata (\x p -> not p) True para' f z = snd . cata (\x (xs, acc) -> (x:xs, f x xs acc)) ([], z)

slide-10
SLIDE 10

Recursion for the Masses

Example: Zygomorphism IV

Finally . . .

there is a scheme for that! running two folds in parallel, one of which depends on the result of the other, can be done by way of a zygomorphism: zygo :: (a -> b -> b)

  • > (a -> b -> c -> c)
  • > b -> c -> [a] -> c

zygo f g z e = snd . cata (\x (p, q) -> (f x p, g x p q)) (z, e) f = zygo (\x p -> not p) (\x e t -> if e then x - t else x + t) True 0

slide-11
SLIDE 11

Recursion for the Masses

Duals and Combinations

So far . . .

we’ve only used initial algebras there are duals for final coalgebras, of course however, there are also schemes combining the two how is that even possible?

slide-12
SLIDE 12

Recursion for the Masses

Data = Codata in Hask I

Turning a Blind Eye

Haskell allows partial functions blurred distinction between data types and codata types allows people to be lazy (no pun intended) "The usual style is to write as if everything is inductive, and if it still works on infinite data, to pat ourselves on the back for using Haskell rather than ML." – Conor McBride

slide-13
SLIDE 13

Recursion for the Masses

Data = Codata in Hask II

Hask,

the category of Haskell types and functions, is algebraically compact basically CPO initial algebras and final coalgebras coincide

  • pens up a cheap way to develop more recursion schemes

but also necessitates some arbitrary choices and a nasty proof theory

slide-14
SLIDE 14

Recursion for the Masses

Example: Hylomorphism I

The scheme

given an F-algebra a and a coalgebra c, morphisms satisfying the equation h = a ◦ Fh ◦ c the so-called hylo scheme morphisms between any two types!

The predicament

however, unique solutions are not guaranteed, even in Haskell but, canonical solutions exist: hylo :: (Functor f) => (f a -> a) -> (c -> f c) -> c -> a hylo alg coalg = cata alg . ana coalg

slide-15
SLIDE 15

Recursion for the Masses

Example: Hylomorphism II

The example: Merge sort Algorithm

1 recursively divide a list into smaller lists 2 when the recursive calls return, merge the resulting sorted lists into

a bigger sorted list

Implementation

divide-and-conquer algorithms lend themselves to the hylo scheme the functor f models the shape of the recursive calls; here: a binary tree an anamorphism for a coalgebra split builds up the tree and a catamorphism for an algebra merge tears it down again

slide-16
SLIDE 16

Recursion for the Masses

Merge Sort

import Data.List import Data.Functor.Foldable (hylo) import qualified Data.List.Ordered as O data Tree a b = Empty | Leaf a | Node b b deriving Functor split [] = Empty split [x] = Leaf x split xs = let (l, r) = splitAt (length xs `div` 2) xs in Node l r merge Empty = [] merge (Leaf x) = [x] merge (Node l r) = O.merge l r mergesort :: Ord a => [a] -> [a] mergesort = hylo merge split

slide-17
SLIDE 17

Recursion for the Masses

Generalisations

Monads and Comonads

recall: all schemes work for arbitrary functors (and their associated data types) but there’s no self-respecting Haskell program without monads recursion-schemes takes that into consideration schemes can be lifted to the (co)Kleisli category of a (co)Monad but: requires distributive laws

Distributive Laws

given monad M and functor F, natural transformation MF → FM dually for comonads recursion-schemes offers basic laws and combinators to build new

  • nes
slide-18
SLIDE 18

Recursion for the Masses

Monadic Iteration

Performing Side-Effects Repeatedly

Elgot monad: monad m with iteration operator of signature (a → m(b + a)) → (a → mb) satisfying certain natural axioms every Monad in Hask is Elgot (by enrichment of Kleisli over CPO) Haskell’s loopM has the type signature above more useful/problematic in more specific settings (e.g. languages without general recursion, with hard-wired effects in the background)

slide-19
SLIDE 19

Recursion for the Masses

Moving to Total Programming

Totality

enables distinction between data and codata removes arbitrary choices in terms of strictness simplifies the proof theory (e.g. e − e = 0 is not always true in Haskell, since e might be ‘⊥‘) but some recursion schemes are no longer applicable Monads are no longer automatically Elgot we need to ensure productivity in (co)recursive definitions of codata

slide-20
SLIDE 20

Recursion for the Masses

Catamorphisms in Agda I

Agda

dependently typed programming language totality ensured by (quite restrictive) positivity and termination checkers

General Definition

in Haskell, data Mu f = In (f (Mu f)) is valid for any functor Agda’s positivity checker rejects this however, initial algebras for polynomial functors can be defined generically data mu_ (F : Functor) : Set where <_> : [ F ] (mu F) -> mu F

slide-21
SLIDE 21

Recursion for the Masses

Catamorphisms in Agda II

Implementation

naive definition of cata fails the termination check! inlining the functor action works

  • nce defined, cata can be used without having to trick the

termination checker! ... mapFold (F1 |x| F2) G a (x,y) = ( mapFold F1 G a x , mapFold F2 G a y) ...

What else can we implement?

dependent version of catamorphisms [Fu, Selinger 2018] hylomorphisms?

slide-22
SLIDE 22

Recursion for the Masses

Recursive coalgebras

Coalgebra-to-algebra morphisms

hylo scheme viewed from a different point of view initial algebra doesn’t have to coincide with final coalgebra defers proof obligation to the programmer, but ensures unique iterate [Capretta et al. 2004] describe several ways to build recursive coalgebras (again, using distributive laws) stronger form: very recursive coalgebras [Capretta et al. 2004]

slide-23
SLIDE 23

Recursion for the Masses

"Elgot Algebras"

"A hylomorphism that cheats"

dual of very recursive coalgebra strictly stronger than the name is actually misleading scheme is the same for the concept of iterative algebras [Milius 2005] difference: unique solutions (iterative alg.) vs. solutions satisfying axioms (Elgot alg.) doesn’t matter for Haskell, but for other languages

slide-24
SLIDE 24

Recursion for the Masses

Beyond Recursion Schemes I

What if . . .

we absolutely need general recursion? example: paperfolds sequence data Stream (A : Set) : Set where _::_ : A -> Stream A -> Stream A toggle : Stream Nat toggle = 1 :: 0 :: toggle interleave : Stream Nat → Stream Nat → Stream Nat hd (interleave s t) = hd s tl (interleave s t) = interleave t (tl s) {-# NON_TERMINATING #-} paperfolds : Stream Nat paperfolds = interleave toggle paperfolds

slide-25
SLIDE 25

Recursion for the Masses

Beyond Recursion Schemes II

This implementation . . .

does not go through with the termination checker in fact, the same definition with the arguments to interleave swapped is not productive

Solution

perform a more cunning analysis of guardedness e.g. [Clouston et al. 2016], guarded lambda calculus:

enriches the type system by a modality ◮ (’later’) elements of ’later’ types can not be accessed ’now’ the signature of interleave becomes StreamG Nat -> ◮ StreamG Nat -> StreamG Nat

slide-26
SLIDE 26

Recursion for the Masses

Last Example

Collatz Sequence as an Elgot Algebra

import Data.Functor.Foldable collatzLength :: Int -> Int collatzLength = elgot algebra coalgebra where algebra Nil = 0 algebra (Cons _ x) = x + 1 coalgebra 1 = Left 1 coalgebra n | n `mod` 2 == 0 = Right $ Cons n (div n 2) | otherwise = Right $ Cons n (3 * n + 1) (courtesy to Vanessa McHale, http://blog.vmchale.com/article/elgot-performance)

slide-27
SLIDE 27

Recursion for the Masses

Selected References I

Edward Kmett recursion-schemes: Representing common recursion patterns as higher-order functions http://hackage.haskell.org/package/recursion-schemes Erik Meijer, Maarten Fokkinga, Ross Paterson (2006) Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire Venanzio Capretta, Tarmo Uustalu, Varmo Vene (2004) Recursive Coalgebras from Comonads Venanzio Capretta, Tarmo Uustalu, Varmo Vene (2009) Corecursive Algebras: A Study of General Structured Corecursion Patrick Thomson (2019) Recursion Schemes blog series https://github.com/patrickt/recschemes

slide-28
SLIDE 28

Recursion for the Masses

Selected References II

Ulf Norell, James Chapman (2008) Dependently Typed Programming in Agda Stefan Milius (2005) Completely Iterative Algebras and Completely Iterative Monads Clouston, Bizjak, Grathwohl, Birkedal (2016) Programming and Reasoning with Guarded Recursion for Coinductive Types Hiroshi Nakano (2000) A Modality for Recursion Peng Fu, Peter Selinger (2018) Dependently Typed Folds for Nested Data Types https://arxiv.org/abs/1806.05230

slide-29
SLIDE 29

Recursion for the Masses

Addendum

Elgot #-algebras

we can generalize Elgot algebras by involving parametrized monads i.e. bi-functors which are monads in one of their arguments hashElgot :: (Bifunctor k, forall a. Monad (k a)) => (k a a -> a) -> (x -> k x a) -> x -> a hashElgot a e = h where h = a . first h . e