Infix lambda

I was playing with syntax today, specifically terse lambdas. After a brief dalliance with Smalltalk-like blocks with a Haskell-like -> separator, I dumped the square brackets and was left with an infix operator for lambda:

successor = (a → a + 1)
fib = memoize (n → if (n < 2) n (fib (n - 1) + fib (n - 2)))
apropos term = filter id
                 (map (f → if (find-substring term (docstring f))
                                  (name f))
                   *module*)

I like it because it's terse without requiring any special parentheses, and it minimizes the difference between an expression and a function abstracting that expression. Like most infix operators, it gets harder to read as the expression gets bigger, but for small expressions it's great - it approaches the brevity of combinators without requiring gymnastics to convert an existing expression to a function. It's also a perfect example of the value of infix: the operator itself serves to delimit the argument list, saving a pair of parentheses.

I vaguely remember having heard of an infix lambda operator before, but casual googling does not reveal it - the results for both "infix lambda" and "lambda infix" are dominated by accidental juxtapositions. Do any of my fearsomely knowledgeable readers know whether and where this has been done before?

I haven't bothered to implement it - I know what it does; I'm trying to figure out what it's like to read. So far I've only found one significant problem: in a language which also has infix define, I'm tempted to read the first argument as the name of the function. Which is just fine, but I tend to confuse the λ and named-λ versions.

Consider (→ body). It's obviously a nice short way to write a thunk: (λ () body). But (f g → body) could be either (λ (f g) body) or (named-λ f (g) body). Neither λ nor named-λ is more right than the other, but there's no reason to choose one - they are both frequently useful, so it's fine to have both. How about for λ and for named-λ, since the double arrow resembles =?

There's also the define/defun question: for (named-λ f (a b) body), do you write (f a b ⇒ body) or (f (a b) ⇒ body)? In prefix syntax, I prefer a separate λ-list: I find (defun f (x) ...) easier to read than (define (f x) ...), because the function name is not mixed in with the semantically different argument names. But in infix I would rather dispose of the parentheses and write the Haskell-like f x = .... This pattern holds for non-defining forms too: I like named-λ to separate the name from the λ-list, but this just looks silly in infix.

The define style also has the advantage that it generalizes nicely to recursive bindings of non-functions:

endless x = (l ⇒ (lcons x l))

Now, does that look familiar? It's the infix version of SRFI-31's rec!

Update 7 Sep: It turns out C# 3.0 has infix lambda, and calls it =>.

No comments:

Post a Comment

It's OK to comment on old posts.