5 ms·
Yes, 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
by qwertyuiop924 10y ago
Yes, 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.
- lispm 10y agoProcedural macros were introduced to Lisp twenty years before CL existed and quasiquotation around ten years before CL existed. > and the splicing unquote is less efficient than cons in this case Not sure why that should be the case and why it even matters.
- qwertyuiop924 10y agoTrue, but as my code aptly demonstrated, procedural macros don't work the same jb all Lisps. Some Lisps don't even have them. AFAICT, splicing unquote has to traverse the entire result list. Not super relevant in this case. I don't know why I picked cons, honestly. I guess it just fit ny mental model of what was happening better.
- JadeNB 10y ago> > Now see, that's much clearer. > From the perspective of someone who knows Lisp, perhaps. For what it's worth, as a perpetual novice programmer who knows Lisp a little and Nim not at all, I also found qwertyuiop924 (https://news.ycombinator.com/item?id=12617350 https://news.ycombinator.com/item?id=12617350 )'s example much clearer. (EDIT: Of course it's totally anecdotal, but I thought that it might be worthwhile to have a data point from someone who is not an expert in either language.)
- qwertyuiop924 10y agoYou'll find lispm's, from a sibling comment, even clearer: (defmacro debug-it (&body expressions) `(progn ,@(mapcar (lambda (expression) `(format t "~%~a: ~a" ',expression ,expression)) expressions))) That's Common Lisp. I probably could have made mine similarly clean, but I was too lazy to do so :-). Anyways, hats off to lispm, because he's the one who actually did it.
- dom96 10y agoYou can also do this (in case that's any clearer): template debugImpl(varName, value: typed) = stdout.write(varName) stdout.write(": ") stdout.writeLine(x) macro debug(n: varargs[expr]): stmt = result = newNimNode(nnkStmtList, n) for x in n: result.add(getAst(debugImpl(toStrLit(x), x))) Whether the Lisp version is clearer or not is very subjective, and you obviously have a lot more experience with it so of course it's clearer for you. To me, the Nim version is far clearer. Edit: flyx's version is even better!
- qwertyuiop924 10y agoI am aware that a template would work in this case (it would also do so in Scheme, but templates are generally less powerful, and I was making a point about imperative macro systems). And yeah, that mixture of templates and macros that you used is semantically similar to what my Scheme code did. But I find Nim discourages writing small templates, which I find myself doing a lot when writing macros in Scheme. Personally, I find that Nim less confusing but still quite awkward.