lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



However at a formal (rather than implementation) level would using an
unadorned token really cause ambiguities? It would be neater without a
special syntax and it should surely be possible to disambiguate this
given that the compiler knows that the token refers to a function (or
not). I'd kind of like to add "mathematical" convention multiplication
too i.e. a b is interpreted as a * b.

First, the compiler doesn't know whether you're referring to a function, and doesn't care since the language is funcitonal. But anyway, the main problem is not theoretical but human: if you put together too much syntactic shortcuts, your programs  become unreadable. People who know Perl or LaTeX must see what I mean...

Extending a syntax is something very error prone. First of all, people tend to do it when they shouldn't, just because technically they can. We tend to optimize code writing rather than code reading (including other people code's reading), although the latter is notoriously harder and happens much more often.

Then, when you add a construct, you often break other ones you didn't think about. For instance with the infix functions you propose:
- how do you deal with associativity and precedence?
- what if someone tries to combine it with implicit multiplication?
- how do you find the end of an _expression_ in a block of statements?
  a plus b
  c times 'foo'

This can be parsed as "(a plus b); (c times 'foo')", or "((a plus b) c (times('foo'))", and probably a dozen other ways. And that's before you integrate any other third party macro. So, you can add a dozen of subsidiary rules, exceptions, restrict yourself to expressions without statements etc. to disambiguate your notation, but you'll spend weeks debugging your hack, any non-trivial usage of it by users will take them ages to debug, and you'll end up with a spec as friendly as a French grammar manual. Not worth it, IMHO.

Lua has an extremely important feature: its syntax is simple to describe and understand, and tries very hard to avoid sources of ambiguity. That's one of the reasons why it's so beginner friendly, and as a by-product, it makes it a great platform for macro programming. Metalua's main purpose is not syntax sugar, but semantic extension of the language; syntax extensibility is just a necessary tool to do so.

Therefore, metalua encourages you to respect lua syntax's spirit, i.e. delimit your constructions with a dedicated initial keyword; macros which respect this rule tend to cohabitate better with the native language as well as with other macros. You can do deeper transformations, but you certainly won't be encouraged to by the parser.

Just to give a recent example of such a goof I did recently, although I really didn't feel like doing something syntactically fancy: I was adding list comprehensions in the language, to write things like this:
function qsort(x)
if #x <= 1 then return x end
local discr = math.random (#x)
local smaller = every n for k, n in ipairs(x) if k~=discr and n < x[discr]
local bigger = every n for k, n in ipairs(x) if k~=discr and n >= x[discr]
return cat (qsort (smaller), { x[discr] }, qsort (bigger))
end
Basically, you can stack as many "if" and "for" clauses as you wish at the end of "every ... ", like you'd do in python inside [...]. It worked smoothly, until I tried to write:
list = every math.random(100) for i=1, 100
for _, n in ipairs(list) do print (n) end

The code above does compile, but not as you'd expect: it puts the statement separator just before the "do"...