3 ms·
The problem isn't having to make sure that you've gensymed everything. I said I'd be okay with er-macro-transformer, from scheme, which has the same problems, a
by qwertyuiop924 10y ago
The problem isn't having to make sure that you've gensymed everything. I said I'd be okay with er-macro-transformer, from scheme, which has the same problems, albeit with proper hygene and with rename instead of gensym, which is nicer (and indeed, in CHICKEN we have ir-macro-transformer, which is like syntax-quote, but better, because it operates on the generated output instead of on the input). The problem is gensym itself.
Most people think that the only axis macro systems rest on is hygenic vs non-hygenic:
Hygenic |Non-hygenic
syntax-rules|defmacro and gensym
This actually isn't true. Macro systems rest on two axies: Hygenic vs Non-Hygenic, and Lowlevel vs Highlevel:
Hygenic Lowlevel |Non-hygenic Lowlevel
er-macro-transformer|defmacro and gensym
ir-macro-transformer|
sc-macro-transformer|
--------------------+----------------------
Hygenic highlevel |Non-hygenic highlevel
syntax-rules |???
Highlevel macros use declarative pattern-matching. Lowlevel macros are imperative. Hygenic macros guarantee that there will be no name-clashes in a rename. Depending on your language, gensym may or may not do this, but usually does. However, hygenic macros also guarantee that all renamed expressions take on the value they have at in the environment within which the expansion occurs. Gensym does not, and never will, do this. This is acceptable in a Lisp-2 with packages (barely), but in a Lisp-1 of any sort, there is an extreme risk of function shadowing, and other nasty bugs (see http://community.schemewiki.org/?hygiene-versus-gensym http://community.schemewiki.org/?hygiene-versus-gensym for details).
Hopefully, this explains why I insist on hygenic macros, and why I think PG really screwed up by not adding hygenic lowlevel macros to arc.
- emidln 10y agoI think the examples I listed (2 of them anyway) actually would be considered hygienic by your standards. syntax-quote as implemented by clojure makes three guarantees: (a) symbols in the body that are postfixed by `#` will be uniformly replaced by a unique gensym in the expansion (b) unquoted things are left alone (c) any other symbol that is not the result of unquote or auto-gensyming, is fully namespace-qualified In so far as is possible with a mutable function namespace (and clojure has this, you can alter the contents of vars and add/remove vars from namespaces), the syntax-quote in Clojure provides the environment within the expansion occurs as well as guaranteeing no name clashes (unless you explicitly opt into them, e.g. unquoting a quoted symbol). If you wanted to do something similar to what the article mentions for common lisp, you end up at: (intern (ns-name 'clojure.core) 'symbol-you-want-to-replace (fn [x] alt-definition-here)) I guess someone technically could do that, but if they do, I would rather my language err towards giving me the power to make bad decisions than hamstring my ability to do useful things. In summary, as long as you have lexical scoping (which FemtoLisp claims to) or namespaces of any sort, you can solve all but the most contrived problems of something like syntax-rules without the additional complexity and restrictions. Again, this can just be added to the language by the user, if they really want it. This comes back to a larger question of, if everything about a language is interesting except its macro system, wouldn't you be well-served to just bolt on the macro system that you desire? (Not saying that FemtoLisp is for you.) Seems that a hygienic vs non-hygienic macro system is fairly trivial if you care enough about the other features.
- deleted 10y ago[deleted]
- qwertyuiop924 10y agoSyntax-quote still isn't actually hygenic by my standards, assuming you can do this (you can test). I don't have a computer in front of me, and I don't know clojure super well (like some of it, don't like some of it, HATE Java and the JVM): (fn a_func [x] (print x)) (defmacro a_macro [y] `(a_func ~y)) (let ((a_func (fn [x] (print "oh noes!")))) (a_macro "this is a string")) ;;;if I'm right, should print "oh noes!" If this does work properly, and no variant of it works, than congrats clojure, you have hygene, or close enough. >I guess someone technically could do that, but if they do, I would rather my language err towards giving me the power to make bad decisions than hamstring my ability to do useful things. Which is why I prefer low-level hygenic macros, like er/ir/sc-macro-transformers: I CAN break hygene whenever I want to, but I have hygene when I want it, and lack of hygene when I don't. Despite what Doug Hoyte may say, I don't have my hands behind my back. >In summary, as long as you have lexical scoping (which FemtoLisp claims to) or namespaces of any sort, you can solve all but the most contrived problems of something like syntax-rules without the additional complexity and restrictions. What additional complexity? With ir-macro-transformer (not mentioned in the article), my macros are as clean as syntax-quote, and it's not that hard to learn. Also, what additional restrictions? I don't see any. >This comes back to a larger question of, if everything about a language is interesting except its macro system, wouldn't you be well-served to just bolt on the macro system that you desire? (Not saying that FemtoLisp is for you.) Seems that a hygenic vs non-hygenic macro system is fairly trivial if you care enough about the other features. Well, femtolisp IS pretty cool, and if you hack together enough code, you can make a system that's almost hygenic, but I'm not sure if you can actually add hygene without the compiler's co-operation. Although gensym may be close enough as far as co-operation goes... I would need to look into this further.
- emidln 10y ago(defn a_func [x] (print x)) (defmacro a_macro [y] `(a_func ~y)) At this point, let won't allow you to rebind a_func (it changes the lexical scope, and the syntax-quote is going to refer specifically to ns/a_func). binding will also not allow it in modern versions of clojure, unless you declare the a_func var as ^:dynamic (e.g. ``` (defn ^:dynamic a_func [x] (print x)) ``` (as an aside, the compiler will also warn you for making something dynamic that doesn't have earmuffs, which is clojure convention)). (ns user (:use clojure.test)) (def a 5) (defmacro inc-by-a [x] `(+ a ~x)) (is (= (inc-by-a 0) 5)) (is (= (let [a 0] (inc-by-a 0)) 5)) ;; the is-macro implementation of thrown? doesn't catch ;; IllegalStateException... (defmacro actually-throws? [exception & body] `(try (do (do ~@body) false) (catch ~exception t# true) (catch Throwable t# false))) (is (actually-throws? java.lang.IllegalStateException (binding [a 0] (inc-by-a 0)))) ;; if we opt into this madness, all bets are off (def ^:dynamic a 5) (is (= (binding [a 0] (inc-by-a 0)) 0))