# Recursion Schemes Workshop

Jean-Remi Desjardins - LambdaConf 2017

## How do you deal with recursive data?

• recursion schemes ðŸ’ª

## Benefits of Recursion Schemes

• define recursive algorithms only once
• decouple howÂ a function recurses over data from whatÂ the function actually does
• avoid general recursion
• concise code

Meijer et. al go so far as to condemn functional programming without recursion schemes as morally equivalent to imperative programming with goto. While comparisons to Djikstraâ€™s infamous letter to the ACMÂ are often inane, the analogy is apt: just as using whileÂ and forÂ loops rather than gotoÂ brings structure and harmony to imperative control flow, the use of recursion schemes over hand-written brings similar structure to recursive computations. This insight is so important that Iâ€™ll repeat it: recursion schemes are just as essential to idiomatic functional programming as for and while are to idiomatic imperative programming.

sumtypeofway.com

## Prerequisite

``````trait Expr
case class NumLit(value: Int)           extends Expr
case class Add(left: Expr, right: Expr) extends Expr
case class Div(num: Expr, denum: Expr)  extends Expr``````
``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]``````
``````data Expr
= NumLit Int
| Div Expr Expr``````
``````data Expr a
= NumLit Int
| Div a a``````

## Exercise #1

``````trait Expr
case class NumLit(value: Int)           extends Expr
case class Add(left: Expr, right: Expr) extends Expr
case class Div(num: Expr, denum: Expr)  extends Expr``````

## Exercise #2

``````trait Expr
case class NumLit(value: Int)           extends Expr
case class Add(left: Expr, right: Expr) extends Expr
case class Div(num: Expr, denum: Expr)  extends Expr

// Increments all numeric literals in provided expression
def inc(expr: Expr): Expr =
expr match {
case Let(id, expr, in) => Let(id, inc(expr), inc(in))
case NumLit(value)     => NumLit(value + 1)
case other             => other
}

## Fix

``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]

//val expr: Expr[Expr[Expr[...]]]
val exprFix: Fix[Expr] = Fix(Add(Fix(NumLit(5)), Fix(NumLit(10))))``````

## Catamorphism

``````trait Fix[F[_]] {
def cata[A](f: F[A] => A)(implicit func: Functor[F]): A
}``````
``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]

val expr: Fix[Expr] = Fix(Add(Fix(NumLit(5)), Fix(NumLit(10))))

// Returns the "complexity" of the expression provided
def complexity(expr: Fix[Expr]): Int =
expr.cata {
case NumLit(value)    => 1
case Add(left, right) => 1 + Math.max(left, right)
case Div(num, denum)  => 1 + Math.max(num, denum)
}``````

## Functor

``````object Expr {
implicit val functor: Functor[Expr] = ???
}``````

## Exercise #3

``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]

val expr: Fix[Expr] = Fix(Add(Fix(NumLit(5)), Fix(NumLit(10))))

case class DivisionByZero(div: Div[Int])

def eval(expr: Fix[Expr]): DivisionByZero \/ Int = ???``````

## Exercise #4

``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]

val expr: Fix[Expr] = Fix(Add(Fix(NumLit(5)), Fix(NumLit(10))))

def collect(a: Fix[Expr]): List[NumLit[_]] = ???

collect(expr)``````

## Exercise #5

``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]

val expr: Fix[Expr] = Fix(Add(Fix(NumLit(5)), Fix(NumLit(10))))

def gen(complexity: Int): Fix[Expr]``````

## Quiz #1

``````trait Tree {
def children: List[Tree]
def transform(f: Tree => Tree): Tree
}

trait Expr extends Tree
case class Let(id: String, expr: Expr) extends Expr {
def children = List(expr)
}
case class NumLit(value: Int) extends Expr {
def children = Nil
}
case class Add(left: Expr, right: Expr) extends Expr {
def children = List(left, right)
}``````

What's the difference over this?

## Exercise #6

``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]
case class Id[A](value: String)      extends Expr[A]
case class Let[A](id: Id[Nothing], as: A, in: A) extends Expr[A]

def eval(expr: Fix[Expr]): DivisionByZero \/ Int = ???``````

Beyond cata (scope)

## Exercise #7

``````case class Fix[F[_]](unfix: ???) {
def cata[A](f: F[A] => A): A = ???
}``````

## Exercise #8

beyond cata #2 (different error message)

``````trait Expr[A]
case class NumLit[A](value: Int)     extends Expr[A]
case class Add[A](left: A, right: A) extends Expr[A]
case class Div[A](num: A, denum: A)  extends Expr[A]

val expr: Fix[Expr] = add(numLit(5), div(numLit(4), numLit(2)))

case class DivisionByZero(div: Div[Fix[Expr]])

def eval(expr: Fix[Expr]): DivisionByZero \/ Int = ???

eval(expr)
``````

Cofree