Closed lihaoyi closed 11 years ago
Did this, but also left the old syntax in for string interpolation (s%"..."
) and pyxl (p%"..."
) to use. All the other macros now use the new syntax, including the the docs and examples.
https://github.com/lihaoyi/macropy/commit/b6015ce6e2fccc8e8142441781158fc32af491cd
Perhaps is using angular brackets worth considering?
sql[(x.size for x in db.countries)]
require[value == 10]
trace[1 + 2 + 3]
f[_ + _]
q[1 + 2]
Not saying this is necessarily better, but it might help distinguish macros from function calls.
If we want to be even more explicit, there's double brackets - q[[1+2]]
.
That's a possibility; I really believe in macros "blending in" to the surrounding code, which is why I wanted require(blah)
to look like the existing assertEquals(blah)
. For quasiquotes [[double square brackets]]
look great, but it doesn't work well for the others.
Also, square bracket's "I'm an array access!" implications may be annoying, when macros generally have more function-like semantics.
On the other hand, square brackets do look really pretty, and probably have an order of magnitude lower chance of collision, and the explicitness (require[blah]
is obviously not an array access) is nice. Honestly not sure what the best way to go is, and changing back and forth is annoying (it was ~1hr of going through all the code, examples and docs).
I think it is important to have macro calls be distinguishable from ordinary function calls, since macros can do weird stuff, which could be really confusing to a programmer if he thinks foo is an ordinary functions. That said, I think macro syntax should closely approximate what the semantics of the macro are, which can differ between macros. For me at least, when I see a function call, my brain automatically recognizes that pattern and tells me that the arguments are evaluated first, then passed to this function. I think square brackets would be a nice reminder that I'm dealing with something different.
One nice thing about using square brackets is that they can be an lvalue (left hand side in an assignment). In particular, you could then unquote names like this: with q: name[foo] = 5
That is a very good point that I did not think of. We can have macros like
match[...] = ...
with q as code:
u[...] = ...
All of which are impossible in either of the current schemes, and necessitates annoying workarounds:
with patterns:
... << ...
with q as code:
x = ...
code[0].targets = [...]
We will need some additional cases to allow the macro-ed LHS capture the entire assignment statement, but I think it is generally useful enough to be an acceptable addition. The Parser Combinator macros could use it, and in general it would allow us to treat
decorate[x] = ...
macros as "decorators" for individual statements, placing the decoration near the name rather than around the body (...
) just like how function/class decorators do it. It's a simple syntactic transform over
x = decorate(...)
but sometimes the difference in readability are significant.
Updated the original issue to include side by side comparisons of all three syntaxes. I think the square brackets looks the best overall, despite being slightly clunky for PINQ (you need nested [()]
or [[]]
). Being able to unquote the left side of an assignment, and using single-line match[]
or peg[]
macros is also very nice. I also think forcing everyone to use one style for macros has an consistency advantage over letting people pick and choose different things, so I'm for all expr macros using macro[...]
syntax.
Pushed this change out, including all the docs and examples, since nobody disagreed enough to object here.
I think everything looks much nicer now using square brackets =)
The original decision to use
macro%...
was due to:%
operations than function calls)Although the
macro%...
syntax still benefits macros such as string interpolation (s%"..."
), it seems the vast bulk of our macros would look much better using themacro(...)
ormacro[...]
syntax instead. This is partially because of the high precedence of the%
operator, turning basically manymacro%...
calls intomacro%(...)
calls anyway:vs
vs
In addition, using
macro[...]
means you can place macros on the left hand side of an assignment, which allows for a nice looking shorthandmatch
andpeg
syntax, rather than having to usewith ...:
block macros when you really only need a single-statement.Due to the fact that you can now rename macros when importing them, the downside of a name collision is much less severe: just rename the macro using
...import macros, sql as macro_sql
or similar.We could:
macros.pct_expr
macros.paren_expr
macros.square_expr
decorators) to allow macros to more closely follow the syntax of similar operations (e.g. functions)