Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I agree with a lot of your points, and I can assure you that many of them are on our to do list including removal of the need for forward declarations. However, I very much disagree with your assessment of Nim's macros, what exactly makes them "really really clumsy?"

I have worked with them a lot and they are very powerful (if a bit verbose), despite this I was able to write a fairly nice implementation of async await and just last weekend I made writing both synchronous and asynchronous IO code much easier using a ``multisync`` macro[1]. This only took me ~100 lines[2].

1: Still a WIP, but already reduces code duplication A LOT: https://github.com/nim-lang/Nim/blob/devel/lib/pure/httpclie...

2: https://github.com/nim-lang/Nim/blob/devel/lib/pure/asyncmac...



Of course they're powerful. They're true imperative macros, the very thing that all Smug Lisp Weenies worship (I don't, because I'm not an SLW, but they are Pretty Cool). The reason I think the macros are clumsy is pretty simple: Nim is really, really bad at manipulating its own AST: It shuffles around, munging data, and by the end, you have no idea what the result should look like. Unlike Lisp, there are no templates in macros, which make seeing what the result will be very easy.

This is how a debug macro, a very simple one, is implemented in Nim (taken from the tutorial):

  macro debug(n: varargs[expr]): stmt =
    result = newNimNode(nnkStmtList, n)
    for i in 0..n.len-1:
      result.add(newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
      result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
      result.add(newCall("writeLine", newIdentNode("stdout"), n[i]))

What the heck is going on? This is the equivalent Lisp (specifically, Chicken Scheme, the dialect I am most familiar with, but it should be similar in most lisps with imperative macros):

  (define-syntax debug
    (ir-macro-transformer
      (lambda (e i c)
        (let ((stmts (cdr e)))
          (map (lambda (stmt)
                 `(begin (write ,stmt)
                         (display ": ")
                         (newline)) 
               stmts)))))
That's much easier to understand. It might be a bit less concise, but it more than makes up for it.

I don't blame Nim for being bad at manipulating its own AST. Lisp is pretty bad at it, too (no, lists are not the lisp AST, not in any lisp suitable for the Real World. I don't know who told you that, but it's a lie: Most lisp ASTs are just as complex as Nim's). What I do blame Nim for is not being potentially homoiconic so there's no easy datastructure to represent code in, or at least providing a datastructure that's easier to manipulate, or at the very least provide some mechanisms to make the existing structures easier to manipulate.


Okay, I did actually get the Scheme macro slightly wrong. That's why I usually try to test this stuff in advance...

  (define-syntax debug
    (ir-macro-transformer
      (lambda (e i c)
        (let ((stmts (cdr e)))
          (cons 'begin (map (lambda (stmt)
                        `(begin (write ,stmt)
                                (display ": ")
                                (newline)) 
                        stmts)))))




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: