[Haskell-cafe] There can be only one fix? Pondering Bekic's lemma

Albert Y. C. Lai trebla at vex.net
Mon Mar 19 17:08:46 EDT 2007


Nicolas Frisby wrote:
> My question is: Given products and a fixed point combinator, can any
> pure expression be transformed into a corresponding expression that
> has just a single use of fix?
> 
> If yes, has there been any usage of such a transformation, or is it just 
> crazy?

Yes.

One use is theoretical and conscious. Facts proven about single 
recursion automatically carry over to mutual recursions, since the 
latter can be packaged up as the former. For example, one fact says that 
regarding the equation x = f x, you can construct the sequence

     _|_, f _|_, f (f _|_), ..., f^n _|_, ...

the terms get closer to a solution for x as you go down the sequence, 
and under a continuity assumption, you just need omega iterations to get 
to the fixed point. The same fact automatically applies to the system of 
equations {y = g z, z = h y}, i.e., the sequence

     (_|_, _|_), (g_|_, h_|_), (g (h_|_), h (g_|_)), ...

gets closer and closer to a solution for (y,z), and under a continuity 
assumption you just need omega iterations to get to the solution. This 
is of course the most basic example of facts. There are more advanced 
facts, such as fusion, leapfrogging, etc. with practical use in code 
optimization, and they are enjoyed by both single recursion and mutual 
recursion.

(Perhaps it is now clear what is meant by "true product" and why it is 
required. The above example fact says in general: start your sequence 
from the bottom. This "bottom" seems a bit ambiguous in the mutual case, 
since there are two different common ways of tupling up two partial 
orders. One is the cartesian product, which you probably know how to do, 
and its bottom is (_|_, _|_). The other is, on top of that --- or shall 
I say below bottom of that --- insert an extra _|_ below (_|_, _|_), and 
this is what Haskell 98 does with its (,) type. Clearly, for our theory 
to work, you want the former, and its probably called the "true product" 
in contrast to the latter, which is the "lifted product" or "pointed 
product".)

But perhaps the most widespread practical use is a subconscious one. How 
do you write an interpreter, for a language that supports mutual 
recursion, in a language that just provides iteration? You introduce a 
program counter and a stack, then you write just one loop: it 
dereferences the program counter, does a case analysis, updates stack as 
necessary, updates program counter as necessary, repeat. You have turned 
a mutual recursion into a single tail recursion. In an indirect sense it 
is an application of the basic example theoretical fact above. It is so 
widespread and yet so subconscious, you cannot avoid it if you use any 
real computer at all, and yet you hardly think about it, as this is 
exactly what every existing CPU does.


More information about the Haskell-Cafe mailing list