Definitely some interesting ideas kicking around.

Alan Manuel Gloria:
> Me, I think it would be better to introduce infix by using some sort
> of marker (say "nfx")
> Basically, infix-processing defaults to OFF, and is turned on only if
> a form specifically says "nfx"
> setq x
>         quote
>               nfx 1 + 2 + 3
> #|parses to:
> (setq x '(+ 1 2 3))
> |#

I thought about that, indeed, there are a number of "infix macros" floating 
around that do that sort of thing.  Having an infix command each time at least 
gives you a "place" to attach precedence/associativity rules.  One minus is 
that having to "turn on" infix every time is really annoying, especially at the 
command line for people who aren't steeped in Lisp.  You're right, it's like my 
"calc()" solution, but I don't like my calc() solution all that much either 
:-).  And you still have the problem of multiple meta-levels with inconsistent 
definitions of operators , needing to define the operators, problems when 
having different interoperating components, etc. :-(.

To counter the "specifying infix each time" issue, you could do "turn on infix 
from now on, for multiple expressions" (an approach you didn't suggest). But 
then you have to store that status information somewhere.  A typical use of 
(read) doesn't expect extra information (nor will it pass that status back 
down).  So now you have to associate "am I infix" with particular streams, 
which adds complexity to the implementation :-(.

> If you're forced to use "calc" anyway
> for some cases, then be regular and force usage of calc for *all*
> cases.

Yeah, I don't like the irregularity either. I agree, bad idea, dump calc().

> > So here's how the rules would look:
> > * If the first non-whitespace character of a new block is "("...

> Hmm.  Yes, the rules do seem unreasonably complex.

Yeah, that's what I was afraid of :-).
 
> Do you happen to have some working code for this?  Just want to see
> how it feels.

Not for this "initial paren" idea.  Indeed, I think you're confirming for me 
that it's a BAD idea, so I DON'T plan to code it up.

But for sweet-expressions, yes, I've got some prototype code.  It's at 
dwheeler.com/readable (a slightly older version is on SourceForge; the license 
is the same, and I intend to post the update to SourceForge when I get a 
chance).  It runs on guile (see the docs).  The working code has some bugs 
involving same-line comments in certain cases; I suspect that the code I reused 
had the bug in the first place :-(.  But the bugs don't impede playing around 
with the idea.

Your comment about () and [] may be the key, though.  Sometimes you need 
infix... and sometimes you don't.  Maybe there's a MUCH simpler rule: [] 
disables indentation while retaining name-prefix and infix, while () disables 
BOTH indentation AND infix. "Normal" users would be told "use [] instead of () 
to surround groups", which wouldn't be too bad.  I agree with you that 
differentiating () and [] makes some sense.  Indeed, I know that Shapiro 
reserved brackets (I think they were square brackets) for just that purpose (to 
choose infix processing).

Now, you COULD support recursively switching between infix and non-infix.  But 
once you disable indentation and infix, you only have name-prefix, and that's 
not nearly as powerful all by itself.  So there's a real allure to saying that 
"(...)" disables ALL sweet-expression processing, and everything inside is ONLY 
a traditional s-expression until its matching ")".  If you do that, then once 
an implementation sees "(", it can immediately call the built-in (read) routine 
instead.  That is a BIG implementation win.  I've found is that when 
implementing sweet-expressions, there's a lot of implementation-unique read 
processing that you have to try to simulate if you want ALL the functionality 
of the underlying system.  That makes it essentially impossible to write a 
portable implementation of sweet-expressions that retains the capabilities of 
the underlying system.  As a side-benefit, most s-expression files would be 
just readable as-is... which I admit is NOT a 'most important' criteria, but 
it'd be nice to get that too.

If I went THAT far, I think it'd be nicer to start with "infix as the default" 
(maybe a callable parameter if you want something else).  That's a reasonable 
default simply because MOST people are more comfortable with it.  Since ANY 
open paren disables the infix until its matching paren, users that use ordinary 
s-expressions might not even notice sweet-expressions.  It's not like normal 
Lispers type in "3 + 4" and expect it to work :-).... so it won't harm those 
who want s-expression prefix, but it will help those who want infix.

So anyway, the rules are simple: Use [] for grouping, which disables only 
indentation; use () only if you want to disable infix, name-prefix, and 
indentation.  Non-Lisp users would typically use only [].  This use of [] 
instead of () is _slightly_ nonstandard, but not terribly so.

So now we're back to nice forms, without any calc()-like irregularities:
3 + 4  ; works, because the sweet-reader starts reading expressions as infix
3 + [5 * 6] ; this worked before, of course... just use [] instead of ().
[3 + 4] * f[x y] ; works.  No more nasty special cases about ().
(+ 3 4) * f(x y) ; this works too (!)

There's another interesting benefit too.  This would mean that 
sweet-expressions would be full of [...] when infix is used... which would make 
them even easier to tell apart from s-expressions.

Interestingly, this also makes it easy to use nfx macros of the kind you 
mentioned. Just tell users that "for advanced infix processing, use (nfx ....) 
and then follow these infix rules (which can then support precedence levels, 
defining infix operators, etc.)".  So now you can have a choice: (1) a built-in 
"base" system doesn't need to be told about operators, precedence, or 
associativity, but you have to group all different operators using [] and you 
can only use punctuation as infix operators... and (2) advanced "nfx-like" 
functions, but one where you have to define all that stuff.

In theory, a program written using Scheme RSR6 might use [...] to mean the same 
as (..).  But I bet almost no one actually DOES that.  And really, it's not 
NECESSARY that sweet-expressions read arbitrary s-expressions.

I could EASILY change my current sweet-expression reader to accept [] and () in 
this way.  Basically, it means that [] would have the current meanings of (), 
and that () would immediately invoke the "old" reader.

> Also, have you considered how Haskell's syntax rules work?  Skimming
> the Haskell syntax rules gives me the impression that Haskell actually
> has nested lists of some sort underneath (it actually has {} forms
> which interact with the indenting rules).

I'm pretty sure that's just syntactic sugar.

> swt-exprs could probably serve a purpose - as a representation in an EDITOR, 
> not
> necessarily in the file. In fact, I would very much prefer to see
> swt-exprs, but I still want to hack the underlying s-exprs. And no, I
> don't mean mixing s-exprs with swt-exprs, but using swt-exprs as ONE
> representation, but if I need to hack the s-expr itself, I just tap a
> button in my editor and it serves it up as s-expr on demand. Sort of
> like using 2/2 for 1. And if I can see the s-expr of a swt-expr, I
> plenty darned well expect to see a swt-expr of an s-expr.

I hacked up an s-expr => swt-expression translator in Common Lisp (see my 
site), but getting a GOOD prettyprinter is harder.

Having a "show the s-expression" button would be TRIVIAL to do in most editors, 
I like that idea very much.

Oh, there _IS_ a potential problem with Barenbaum's idea about colons: Colons 
(esp. colon prefixes) have special meaning in lots of Lisps.  I'd rather not 
require extra characters for common cases when they aren't needed; I think JUST 
indenting is enough to give meaning.  But I don't want to cut off any ideas; I 
think discussing/trying out "crazy" ideas is necessary to get to a good 
solution of this problem.

--- David A. Wheeler

Reply via email to