-
-
Save disnet/6735679 to your computer and use it in GitHub Desktop.
var random = function(seed) { /* ... */ } | |
let m = macro { | |
rule {()} => { | |
var n = random(42); // ... | |
} | |
} |
I suppose what I had in my mind was a mixture between the two. I was thinking a macro whose internal declarations were in fact hygenic (didn't leak out), but whose references to non-internal (aka, external) variables were not closured and instead just adopted the scope where they were expanded.
That model would prevent a macro that you use from accidentally overwriting something in your scope that is unexpected, but still let you provide in your own scope any other scoped bindings for any external references it may have.
To put it in the parlance of your post about variable renaming, you'd rename any variables that are declared inside a macro, but you wouldn't rename any other references which weren't declared there.
That would sort of turn var
into a let
that bound itself to the scope of the macro's (implicit) block, while still letting in scope from outside the macro for external references.
I can think of quite a few cases where I would use such a model.
In fact, instead of renaming macro-declared variables to prevent overlap, you could just wrap (while expanding) the body of the macro code in a stand-alone { .. }
block and change var
s to let
s, and that would create hygiene (aka, prevent any inner-to-outer leakage) in the same way as renaming, but with way less work.
So what you are asking for ("non-lexical macros") are unhygienic macros. You can build them if you'd like. Sweet.js has features that let you specifically write them (see the homepage for a mini-tutorial on breaking hygiene, it shouldn't be very difficult to define a
macroUnhygienic
if you really want). But in general they are very very bad and I don't think it's a good idea to include a macro form that does them "by default" with sweet.js.Here's the thing. Hygiene (of the sort I've described here) is essential in building macro abstractions that play nice together and work in any context. Breaking hygiene and doing a "find-n-replace" expansion means that the macro author must reason about every possible context in which the macro could be invoked. This is bad in ways you might not have thought about yet.
function
could be rebound,var
could be rebound. How can you write a robust macro when you can't even be sure thatfunction
means what you thought it meant?This isn't equivalent to late-binding
this
. When you late-bindthis
there is an implicit contract on what is expected to go on the object/prototype chain. You are explicitly calling out the delegation that is occurring by usingthis
and prototypes. Not so with an unhygienic macro because the potential delegation is everything in scope. This is impossible to reason about.A hygienic macro system like sweet.js by design and by default protects the abstraction capability of macros. It provides escape hatches when you really need to break hygiene but this should only be done when you absolutely must.