I like functional programming, especially in points-free style: it allows amazingly terse expression, with minimal attention to plumbing. But there is an annoyance that often trips me up: there's a discontinuity between combinator expressions and lambda-expressions.
Consider the commonplace task of iterating over a list and, say, printing out the elements. There is an obvious points-free way:
(mapc #'print numbers)
More complex functions can be handled with combinators...
(mapc (compose #'print #'round) numbers)
...and partial application:
(mapc (compose (op format t " ~S~%" _) #'round) numbers)
But for more complicated functions, points-free style gets awkward quickly, especially if you don't have other combinators like s
. As soon as we want to use some variable twice, it's usually easier to switch to lambda
:
(mapc (lambda (x) (format t "~S (really ~S)~%" (round x) x))
numbers)
And that switch requires rewriting the whole function in a completely different way.
Having more than one way to write functions is not in itself a problem, of course. The trouble is that I often want to transform between them, when a points-free expression grows to the point where it needs a lambda. At this point I have to stop thinking about what I want the code to do, and instead think about the trivia of combinators and variables. It's only a minor annoyance, but it's a very common one. And it's more common in more points-free code, so it probably discourages good programming style.
Is there an alternative to lambda/combinator calculus that can express simple functions as tersely as combinator style, but without the discontinuity when they grow? In particular, I want to be able to name variables without reorganizing the code that uses them. This is a hard problem and I don't expect to find any good solution, but I'm still looking. Lambda and combinator calculus are wonderful things, but they're not perfect, and even a small improvement in expressiveness of simple functions would be a big deal.