Matthias van der Hallen
The main idea is that co-recursion is the dual of recursion;
but in what sense?
Recursion
Co-recursion
fibonacci :: Int -> Int
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n-1) + fibonacci (n-2)
A recursive definition of fibonacci breaks down its argument and returns a number
The idea of this being a 'fold' is probably more relatable by looking at natural numbers as a Peano numbers
(e.g. \(S(S(S(S Zero)))\))
co-fibonacci :: Int -> Int -> [Int]
co-fibonacci a b = a `cons` co-fibonacci b (a+b)
A co-recursive definition of fibonacci builds an infinite list of fibonacci numbers by specifying part of the output
This function 'unfolds' a pair of integers into an (infinite) list of integers.
Note that in a language with support for codata, e.g. with lazy evaluation, infinite lists are not a problem.
(Literature suggests a link here with Prolog & tail recursion module cons - think of an accumulator - and possibly bottom-up / top-down through that)
Example: sorting functions with type
sort :: [a] -> [a]
NatListF f = Nil | Cons Nat f
Using open recursion instead of explicit recursion, and specializing to natural numbers, we can write
The least fixpoint is written as \( \mu \mathit{NatListF} \), while the greatest fixpoint is written as \( \nu \mathit{NatListF} \).
List a = Nil | Cons a List
We recognize this as the definition of a list:
NatListF f = Nil | Cons Nat f
The least fixpoint is written as \( \mu \mathit{NatListF} \), while the greatest fixpoint is written as \( \nu \mathit{NatListF} \).
Folding corresponds to using the isomorphism between \(\mu F\) and \(F (\mu F)\), as well as the algebra \(F a \rightarrow a\)
Unfolding corresponds to using the co-algebra \(a \rightarrow F a\), as well as the isomorphism between \(F (\nu F)\) and \(\nu F\)
A slightly more complicated unfolding (futumorphism, *) can take into account the structure already unfolded.
(*): Every futumorphism can be rewritten into a (generalized) unfolding
Example: How to 'grow' a plant according to a complex set of rules based on one 'seed' value https://bit.ly/3ber6zv
Looking at the seed, it (randomly) branches, blooms or grows straight, leaving a new seed at the end of the structure
Note how at every 'iteration' (step), it looks as if we recurse through our result and expand where necessary; we co-recurse
Nevertheless, I hope we all learned something
No (?)
The paper "Mechanizing Coinduction and Corecursion in Higher-order Logic" states:
"With corecursion, the case analysis is driven by the output list, rather than the input list."
That definitely makes me think of function definitions like:
{
f(a,b) = pTrue <- p(a) & p(b).
f(a,b) = qTrue <- q(a) & q(b).
}
But I don't see it yet...