recursion and corecursion

Recursion consumes data whereas corecursion produces data. Corecursion can be used with laziness feature to implement infinite data structures.

Recursion starts from the original parameter, call itself until it reaches a base case. It’s from up to bottom.

Corecursion works the opposite. It starts from an initial state and transform the state to build next value and continues the transformation step by one step. It’s from bottom to up. Corecursion is more like we define math theory. e.g. for fibonacci number,

f(0) = 0 // initial state
f(1) = 1 // initial state
f(n) = f(n-1) + f(n-2) // induction

Recursion fib

def fib(n: Int): Int = n match {
	case _ if n <= 1 => n  // base case
	case _ => fib(n-1) + fib(n-2) // decrease n step by step
}

Corecursion fib

def fib(n: Int): Int = {
  def transform(state: (Int, Int), n: Int): (Int, Int) = {
    if (n < 1) state
    else transform((state._2, state._1 + state._2), n - 1)
  }
  transform((0, 1), n)._1
}

Above is not an evident corecursion implementation. If we have a lazy list Stream[+A] defined. We can write the implementation as below.

def cons(h: => A, t: => Stream[A]): Stream[A] = ???

// fib is corecursive, cons is a stream constructor with lazy parameter
def fib(a:Int, b: Int): Stream[Int] = cons(a, fib(b, a + b))

def fibnacci(n: Int): Int = fib(0, 1).take(n).toList.last