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.