Recursion is a concept that often used in programming. Usually, when people said the problem could be solved recursively — they meant the problem could be broken down into a smaller problem.

A lot of the algorithms and data structures are usually solved in iteratively and recursively.

A lifecycle of project management usually are conducting in an iterative approach. An extensive system can be broken down into multiple microservices. The most popular data structure, arrays, is a recursive data structure by nature.

For instance, imagine you are having a coding interview for an X company, and they ask you about the classic Fibonacci questions.

“Given a number n, print nth Fibonacci Number.”

The first thing that pops in your head about solving this problem is either recursively or iteratively (Dynamic Programming). However, there is also another way of answering these questions using corecursion.

You must be thinking:

“So, what is Corecurion?”

In this article, I will share what corecursion is and give you two examples of solving problems with corecursion.

What is Corecursion?

Corecursion is the opposite of recursion. Whereas recursion consumes data, corecursion produces data.

Recursion usually works analytically. Starting from the data or problem that is further from the base case, you break down the problem into a smaller data by using induction until it hits the base case.

Corecursion works synthetically. Starting from the base case and build it up — iteratively produces data further away from the base case with some recurrence relation.

A classic function of corecursion is unfold:

def unfold[A,S](s: S)(f: S => Option[(A,S)]): List[A]

The A is the result of the state after applying f, and S is the current state or next state after using f.

It takes an initial state, and a function for producing both the next state and the future value in the generated iterator, be it List or Stream.

One way of thinking about recursion vs. corecursion is to solve the problem as a top-down approach and a bottom-up approach.

Let’s implement unfold:

def unfold[A,S](s:S)(f: S => Option[(A,S)]): List[A] = f(s) match {
	  case Some((currentResult,nextState)) => currentResult :: unfold(nextState)(f)
	    case None => Nil
	  }
view raw
unfold.scala hosted with ❤ by GitHub

With the classic Fibonacci number, we can generate n Fibonacci number with unfolding by starting from 0 and 1 and adding both numbers to produce the next state:

def fib(n:Int): Int = generateFibSequence(n).last

	def generateFibSequence(n:Int): List[Int] = 
	  unfold((n, (0,1))){s => s match {
	    case ((num, (f0, f1))) if(num > 0) => Some((f0, (num -1, (f1, f0+f1))))
	    case _ => None // terminate
	  }}
view raw
fib.scala hosted with ❤ by GitHub

In the above function, we generate a Fibonacci sequence by keeping track of the n number and two of the previous state to produce the next Fibonacci state. The function will terminate when num reaches 0, meaning we provide n Fibonacci sequence.

Corecursive are often widely used in functional programming, primarily when dealing with an infinite data structure, such as Stream-building function. Often, corecursion gives the solution of producing a finite subset of a potentially infinite structure, such as creating Generator.

I will give two other examples where we can use corecursion to solve the problem.

#functional-programming #programming #algorithms #recursion #scala #function

WTF is Corecursion? What is Corecursion?
1.05 GEEK