My approach to readability combines 3 basic approaches, and I've recently
realized that they can be described and layered separately... and I plan to
describe them that way. I've also implemented layers #1 and #2. I'd love to
hear feedback on all this, so here we go...
Basically, I think my approaches can be grouped into 3 alternatives, which
layer on each other:
1. Curly infix. Expressions in {...} become infix (always), so {3 eq? 4}
becomes (eq? 3 4). This ONLY provides infix (and obviously there are other
ways to do this too). But since it uses the unused {...}, it's perfectly
backwards-compatible... you could add this to any existing reader without
harming anything else.
2. Modern-expressions, aka mod-expressions. This adds "term prefixing" - if a
symbol or list is followed (without whitespace) by (), [], or {}, then it has a
different meaning. In addition, unprefixed [..] also creates lists. So f(x y)
becomes (f x y), f{x + y} becomes (f (+ x y)), etc. This is generally
backwards-compatible with nicely-formatted code, but it IS a change, and badly
formatted code like (A(B)) would be interpreted as ((A B)) instead of (A (B)).
3. Sweet-expressions. This takes modern-expressions and adds indentation
(I-expressions).
I have posted a production-ready implementation of "curly infix" for Common
Lisp; Common Lisp's macro characters make it easy to support them without much
fuss.
This morning I've posted on the svn server a first-cut implementation of
"modern-expressions" in Scheme, as "modern.scm". It's still a little wet
behind the ears. but it passes 81 test cases... so it should be reasonably
robust.
Unfortunately, to implement modern-expressions in most Scheme implementations,
you have to completely re-implement Scheme's "read" function, because most
Scheme readers don't consider [,],{, or } as delimiters. That's too bad;
implementing mod-expressions actually doesn't take much code (most of my
implementation is just a re-implementation of "read"). Still, "read" is not
THAT hard to re-implement, so I've done so. A disadvantage is that you can't
use whatever extensions of the reader exist while using modern-expressions (aka
mod-expressions). In the long term, I'd like these readers built into the
implementation, but this is MORE than good enough for experimentation and
eventually real work.
I believe that once we have a properly-working I-expression reader, it should
be able to directly build on modern-expressions, automatically creating a
sweet-expression 0.2 reader. Yum.
I've now done a lot of experimentation to create test cases, etc. Based on that
experience, I now have the following opinions:
* (...) should NOT normally disable modern/sweet-expressions; at best, an
optional mode could implement that. Although it's a nifty fop to
backwards-compatibility, it's absurdly easy to use (...) for a list when you
did NOT intend to disable mod-expressions. What's worse, it's really hard to
find which parens actually did the disabling. It's much better to have both ()
and [] just mean "make a list" - this is consistent with much practice (Scheme
R6RS even enshrines this).
* Prefixing of (), [], and {} should ONLY be acceptable if the prefix is a
symbol or list; otherwise, ignore it. It's exceedingly unlikely that someone
who wrote 9(i) meant (9 i).
Thoughts? Comments?
--- David A. Wheeler