Out of curiosity, could someone who's written in both languages, give their opinion on the differences between the two macro systems?
* Nim generics = parametrization-by-type of procs (functions) or other type definitions. Happens at compile time. Like Java generics or C++ templates, except that it uses [ ] rather than < >.
* Nim templates = direct textual substitution of code at the call-site at compile-time, like the C preprocessor, except that: 1. It operates upon a parsed Nim AST rather than plain-text code; 2. It's (by default) hygenic; and 3. The syntax is the same as regular Nim language syntax (in contrast to the crippled C preprocessor syntax).
* Nim macros = compile-time evaluation of code to perform side-effects, one of which may be inserting new code at the call-site. Nim macros are most like Lisp macros. An invoked Nim macro receives a parsed Nim AST as a tree data-structure, and is able to traverse & manipulate that AST, or create & output a new AST. When evaluating macros, the Nim compiler runs the macro code in a compile-time Nim interpreter, so macros can invoke any other functions, allocate data-structures, etc. And again, the syntax is the same as regular Nim language syntax.
[There's another Nim language feature that I really like, which I think is worth mentioning here: the `const` keyword, to define constants. Nim provides `var` to define variables that are read-write storage boxes, and `let` for single-assignment storage boxes. `const` is like `let`, except it's evaluated at compile time. This means you can evaluate arbitrarily-complicated expressions (including function calls) at compile time, obtaining the result as a constant of the appropriate result type, which can then be inlined at all usage-sites -- just as if you'd entered the literal value directly in your code.]
With this background in place, I can finally get to my main point:
When I'm getting excited about Nim to friends, I tell them that I think Nim macros are the best tradeoff between an expressive Python-like syntax & powerful AST-based, Lisp-like macros.
You see, no-one would dispute that it is very elegant to use regular function syntax to operate upon homoiconic code as a data-structure. And no-one would dispute that operating upon a pre-parsed AST is superior to crude text-concatenation (like in the C preprocessor). But as a commenter on HN pointed out in a recent thread about Lisp:
"""So, the real question is why did such a magical language lose to the upstarts that all appeared in the late 80's and early 90's: Perl, Python, Tcl, Lua, etc. Answer: files, strings, and hash tables. All of those languages make mangling text pathetically easy. Perl is obviously the poster-child for one-liners, but all of those make that task pretty darn easy. Lisp makes those tasks really annoying. Just take a look at the Common Lisp Cookbook for strings: http://cl-cookbook.sourceforge.net/strings.html """ -- https://news.ycombinator.com/item?id=11700176
Dense language syntax is beneficial because it enables brevity for frequently-occurring operations. For example: string indexing, slicing & especially regex matching, if you do a lot of text-processing; inline arithmetic operators if you do a lot of arithmetic; and array operators, if you do a lot of matrix processing.
Nim's macros combine a dense (Python-like) language syntax with powerful AST-based macros, enabling you to traverse & manipulate the AST just like any other data-structure.
For example, decorators and context managers can do very similar things but make things easier in many cases in Python.
As far as I can see, the Python community has done a pretty good job of using magic sensibly. The Ruby and C++ communities, not so much.