Friday, May 1, 2009

Interlude

Part III is coming, but I wanted to make an observation. As I've been writing stuff, I've been poring over a lot of code looking for examples and counter-examples of ideas I want to talk about. Since I've been talking about tail recursion, I've been looking in particular at procedures that return answers that are computed by other procedures. It's fairly easy to find these things — you just look for a return statement immediately followed by a function call: return<whitespace+><identifier><whitespace*> (. This doesn't match perfectly, but it gets more than enough results to look at. In looking at several examples of code I've noticed a common pattern like this:
    ...
    Type foo = computeFoo (a, b);
    return foo;
}
This isn't a tail recursive call, but it is trivially transformed into one. Then I was thinking that I wouldn't write code like that. (At least I don't think I would.) It's like writing this in Scheme:
    (let ((foo (compute-foo a b)))
       foo)
Which is simply syntactic sugar for this:
    ((lambda (foo) foo) (compute-foo a b))
Which trivially beta-reduces. That first term is better known as the identity function, so I could have just written:
    (id (compute-foo a b))
instead of the let expression, but why would I even bother? So this code:
    ...
    Type foo = computeFoo (a, b);
    return foo;
}
is really a verbose way of writing this:
    ...
    return Identity<Type> (computeFoo (a, b));
}
which no sane person would prefer to
    ...
    return computeFoo (a, b);
}
Yet there are apparently reasonably sane people who seem to prefer the first version. (caveat here: I'm assuming that the Identity function is being called for value and that this is not an obscure means of forcing a type coercion. In all the code I've looked at, I haven't seen anything that nasty, but it wouldn't surprise me. I'm not talking about obscure compiler tricks.)
Post a Comment