Thursday, January 14, 2010

The fixed-point combinator drives me nuts. I've figured out the principle several times now, only to have it escape out the other ear in under a minute. So I'm going to document it here to help myself understand it better.

All code is in Python.

So, the basic problem is how to implement a recursive algorithm with a non-recursive function. The fibonacci sequence is a good example of this:


def fib(n):
return n if n < 2 else fib(n - 2) + fib(n - 1)

print fib(10)


So, how do we turn this function into a non-recursive one? A first attempt might look like this:


def fib(f, n):
return n if n < 2 else f(f, n - 2) + f(f, n - 1)

print fib(fib, 10)


So the basic idea is to pass fib itself so that it doesn't have to know its own name in order to call itself. Note, however, that we have to pass f to itself every time, even in the kernel of the fibonacci algorithm. Ignoring this for now, the proof that we have eliminated direct recursion is in the fact that we can now define fib using a lambda:


fib = lambda f, n: n if n < 2 else f(f, n - 2) + f(f, n - 1)

print fib(fib, 10)


There is an obvious problem here: it's rather annoying to have to pass fib to itself in order to recurse. A wrapper function fixes this:


def fib(n):
f = lambda f, n: n if n < 2 else f(f, n - 2) + f(f, n - 1)
return f(f, n)

print fib(10)


We don't want to have to write a wrapper function for each recursive function we want to write. Can we write a generalised function that will take our fibonacci and any other recursive algorithm kernel and convert it into a recursive function? As a stepping stone, let's change our latest fib to a function creator, rather than a simple wrapper:


def fib_maker():
f = lambda f, n: n if n < 2 else f(f, n - 2) + f(f, n - 1)
return lambda n: f(f, n)

fib = fib_maker()


We can see that the fibonacci kernel is just a function inside the fib_maker function. So let's replace fib_maker with a general-purpose recursive function generator:


def recursive(f):
return lambda n: f(f, n)

fib = recursive(lambda f, n: n if n < 2 else f(f, n - 2) + f(f, n - 1))


It seems that we are almost there. We must now tackle the hard part, that we've been putting since near the beginning. The lambda should not have to forward the recursive function to itself. We want to be able to write this:


fib = recursive(lambda f, n: n if n < 2 else f(n - 2) + f(n - 1))


Clearly, the function being passed in has to be a full copy of the created recursive function. Allowing, 'recursive' to use recursion itself makes this quite simple:


def recursive(f):
return lambda n: f(recursive(f), n)

fib = recursive(lambda f, n: n if n < 2 else f(n - 2) + f(n - 1))

print fib(10)


(Note carefully that the nested call to recursive(f) occurs inside the lambda n. If you try to compute it in advance, you'll trigger an immediate infinite recursion and blow the stack.)

But can we go one step further, and define 'recursive' without directly using recursion? Loaded question. It turns out that our original trick of passing a function to itself to help it recurse is just the ticket.


def recursive(f):
def recurse(r):
return lambda n: f(r(r), n)
return recurse(recurse)


Further reductions are possible:


def recursive(f):
recurse = lambda r: lambda n: f(r(r), n)
return recurse(recurse)

def recursive(f):
recurse = lambda r: lambda n: f(r(r), n)
return (lambda R: R(R))(recurse)

recursive = lambda f: (lambda R: R(R))(lambda r: lambda n: f(r(r), n))


Finally, we have arrived at a non-recursive function that converts other non-recursive functions into recursive algorithms! This is what's commonly known as a fixed-point combinator, renamed here and Pythonified:


fix = lambda f: (lambda R: R(R))(lambda r: lambda *args, **kwargs: f(r(r), *args, **kwargs))

fib = fix(lambda f, n: n if n < 2 else f(n - 2) + f(n - 1))


Footnote: This is not quite the fixed-point combinator you'll see written up elsewhere. Usually, the kernel is written as a nested lambda and passed to a Z-combinator, thus:


fib = Z(lambda f: lambda n: n if n < 2 else f(n - 2) + f(n - 1))


This is a minor detail, however. The principles are the same. Also, the (lambda R: R(R))(lambda r:...) is my special touch. Z-combinators usually pass the lambda r:... to a full copy of itself.

Correction: The Z-combinator is not quite the same thing. This warrants further investigation.

Saturday, December 19, 2009

A new beginning

Hmm... "slow start" is probably an understatement. Let's try again...

Computer technology is in my veins, and I'm not your usual flavour of programmer. I tend toward a contrarian philosophy. I tell myself it's because everyone else is getting it all wrong, but in my weaker moments I admit the very slight possibility that a small subset of "everyone else" might have a point here or there.

In this blog, I will describe and defend my views, partly to reinforce and (hopefully) organise them in my mind, and partly to expose them to the cold hard light of day, that the weak may perish and the strong photosynthesize.

I'm going to start off by briefly listing the problems with I.T.:
  • XML
  • SQL (not relational) databases
  • Java
  • XML
  • OO
  • Threads
  • XML
Have I mentioned that I don't like XML? Actually, that's only partly true. The other part is that I hate, loathe, detest, abhor, and would very much like to put a stake through the heart of XML. Those insipid little angle brackets just sit there grinning smugly, looking innocent and benign, whispering sweet lies to me: "Trust me, I am the end of all your tears. I can safely hold all your email, your documents, your databases and bank accounts. Just give in to me, and the pain will all go away." Only it doesn't. The pain gets worse; much, much worse. Don't believe me? Spend a few months programming XSL, or diagnosing broken SOAP packets, or configuring some big fat object graph with an XML-based DI framework.

Why, oh why, did we all fall for this putrid, festering disease of a standard? Almost anything would have been better. S-expressions have been around since the late 60's for goodness sake! Instead of this:



<person>
<name>
<first>John</first>
<last>Doe</last>
</name>
<dob>1984-04-04</dob>
<status>married</status>
</person>


We could have had this:



(person
(name (first "John") (last "Doe"))
(dob 1984 4 4)
(status married))


Is there a self-respective programmer anywhere who would argue that the XML is better? Cleaner? Easier to read and write? I can't think of a single dimension along which XML ranks better. Other than popularity, of course, which is precisely my point. How does such an fetid, rancid idea take root and become the lingua-franca of the programming planet? Answer me that!


If you need more convincing, consider this. To even enter the above XML into Blogger, it wasn't enough type it in as you see it, I had to type this:


<pre>
&lt;person&gt;
&lt;name&gt;
&lt;first&gt;John&lt;/first&gt;
&lt;last&gt;Doe&lt;/last&gt;
&lt;/name&gt;
&lt;dob&gt;1984-04-04&lt;/dob&gt;
&lt;status&gt;married&lt;/status&gt;
&lt;/person&gt;
</pre>


And you don't want to know what I had to type just above in order to show you what I had type further up. If the whole HTML roadshow had started off with S-expressions, typing a literal S-expression into an S-expression-based document could have been as simple as:


'(person
(name (first "John") (last "Doe"))
(dob 1984 4 4)
(status married))

Thursday, May 24, 2007

A beginning

I'm not into blogs (nothing I could say warrants spamming the world) but the world is, so I dip a toe. Don't come for pearls; I'm just another person. My opinions lack any distinguishing characteristic, being neither particularly thoughtful or thoughtless, radical or conservative, important or trite. Don't come at all unless you're the sort that likes to while away the hours scraping at the stuff under your nails; this should be more amusing than that. Slightly. Maybe.