4 ms·
Yes, but Nim's macro system, while very powerful, is, at present, the most clumsy and awkward thing I've seen. it's imperative, like Lisp macros. However, unlik
by qwertyuiop924 10y ago
Yes, but Nim's macro system, while very powerful, is, at present, the most clumsy and awkward thing I've seen. it's imperative, like Lisp macros. However, unlike Lisp macros, there are no templates, and the datastructure that Nim's macros manipulate is much closer to the actual AST, and thus far more complex. Because of this, Nim macros must clumsily plug together an AST, and do so in a manner that's so noisy that you have to squint to see what it's actually doing by the time you're done. Meanwhile, Lisp macros are not necessarily shorter, but it's far easier to see what's going on.
- mrkgnao 10y ago> much closer to the actual AST I thought Lisp code was its own AST, although I don't really know much about Lisp.
- qwertyuiop924 10y agoMany people claim that, but no Lisp compiler or interpreter beyond toy level actually uses it as such: It doesn't provide a lot of the syntactic information compilers need. However, Lisp code as AST is an excellent abstraction for macros, so that's what macros manipulate.
- progman 10y ago> However, unlike Lisp macros, there are no templates That's wrong. Nim has both macros and templates. > Lisp macros are not necessarily shorter, but it's far easier to see what's going on. Even Lisp macros are not perfect. You always have to care for new symbol names where the lexical scope could be affected.
- qwertyuiop924 10y agoYes, but Nim doesn't have imperative templates, so its template system is fairly limited. As for hygene problems, Nim's system has the same issues. Here's an example of the problem: This is a simple Nim macro from the tutorial: macro debug(n: varargs[expr]): stmt = result = newNimNode(nnkStmtList, n) for x in n: result.add(newCall("write", newIdentNode("stdout"), toStrLit(x))) result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) result.add(newCall("writeLine", newIdentNode("stdout"), x)) What the heck is that doing? Now let's compare to my lisp of choice, Chicken Scheme (although this macro would be much the same in any Lisp with imperative macros) (define-syntax debug (ir-macro-transformer (lambda (x i c) (let ((exprs (cdr x))) (cons 'begin (map (lambda (expr) `(begin (write (quote ,expr)) (display " : ") (display ,expr) (display "\n"))) exprs)))))) Now see, that's much clearer. And before you ask, yes, this macro is completely hygenic, because it uses ir-macro-transformer. Don't worry too much about it if you're not familliar with Chicken Scheme. It's not the important part.
- flyx86 10y agoNow see, that's much clearer. From the perspective of someone who knows Lisp, perhaps. I find it very confusing. Moreover, you are using an example from a tutorial, which is meant to explain how things work, not to show what the best way to write some macro is. You can write the same macro with less verbosity: macro debug(n: varargs[typed]): typed = result = newNimNode(nnkStmtList, n) for x in n: let xRepr = toStrLit(x) result.add(quote do: writeLine(stdout, `xRepr` & ": " & $`x`))
- qwertyuiop924 10y agoAh, thanks. I didn't mean to misrepresent Nim, but I don't know the language super well. It seems Nim has a code templating system, which is nice, but I find a bit more confusing and less pleasant than the Lisp equivalent. As I said before, it just feels clumsy, especially compared to Lisp.
- lispm 10y agoThat's not too far away from Lisp. But in Lisp we would be using some quasiquote templates for this simple macro: (defmacro debug-it (&body expressions) `(progn ,@(mapcar (lambda (expression) `(format t "~%~a: ~a" ',expression ,expression)) expressions))) Example: CL-USER 8 > (pprint (macroexpand-1 '(debug-it (+ 1 2) (* 3 4)))) (PROGN (FORMAT T "~%~a: ~a" '(+ 1 2) (+ 1 2)) (FORMAT T "~%~a: ~a" '(* 3 4) (* 3 4))) CL-USER 9 > (debug-it (+ 1 2) (* 3 4)) (+ 1 2): 3 (* 3 4): 12 NIL
- qwertyuiop924 10y agoI could have done that, but I couldn't be bothered to look up the interpolation strings for format (yes, we have it), or the one level flatten function, and the splicing unquote is less efficient than cons in this case. But yes, in real code, I'd probably go in that direction. There is one key difference between my code and yours: in mine, the functions, values, and symbols referenced in the macro are automatically renamed, thus guaranteeing no namespacing conflicts. Due to the way the CL package system works (IIRC), CL provides almost the same guarantees, at least in this case. But it is an important semantic difference. And it is the Common Lisp. Despite how you may feel, Scheme, Clojure, PicoLisp, NewLisp, Racket, Interlisp, LeLisp, EuLisp, and others are as much a Lisp as CL.