Open vshesh opened 9 years ago
Yes this has being one of the most painful blockers in getting a macro imports working and in general making macros really useful. Right now defmacro
is a macro itself defined here:
https://github.com/Gozala/wisp/blob/0bcaeb6376086314a13b51a3c7b5ac507f5209f9/src/backend/escodegen/generator.wisp#L53-L65
The issue is that defmacro
evaluates macro function in the scope of that module rather than in the scope it is defined in. There has being some effort to fix this by compiling (defmacro ...)
to regular functions (see #102) instead although there is still work to be done.
I'm afraid I don't have much time to dedicate to this, but I'm more then happy to help to any volunteer ;)
To be clear that problem is quite tricky and it requires proper implementation of compile and runtime phases. Meaning that during complete time defmacro
form can't be just converted to function, instead it needs to be analysed for all the references & those references need to be also compiled and evaluated so that macros could make use of them. In practice this is even more tricky as references may be imported from other modules there for compiler would need to compile imported modules first.
I suspect simple hacky implementation could be made by changing defmacro
so that it just compiles to a definition (as illustrated in #102) and does not actually eval but rather eval the full module incrementally form after form afterwards (so that forms after macro could make use of that macro)
Ok, I don't know enough about macro systems to work on this, but I'm curious how the chain macro example works then, since it calls reduce/cons/first/rest and those all have to be imported.
@vshesh Well, through the use of reader macro's for the most of it I guess. Syntax-quote just does that. But also since there is a precompilation process which makes it a lot easier. I added the chain operator (or thread-first macro as its called in Clojure) to src/expander.wisp
and this is how it looks after compilation. Since I have npm link
, I can npm link wisp
in other projects and use it locally everywhere because this is what it compiled down to:
var expandThreadFirst = exports.expandThreadFirst = function expandThreadFirst() {
var operations = Array.prototype.slice.call(arguments, 0);
return reduce(function (form, operation) {
return cons(first(operation), cons(form, rest(operation)));
}, first(operations), rest(operations));
};
installMacro('->', expandThreadFirst);
And they would work for a few reasons, one being JS is lenient with non-existing objects and functions and given the correct context, suddenly (when executed there) the variables may be around in scope or, even more importantly I guess, because they are referenced explicitly in the expander.wisp source. I've had more than enough cases where I needed to reference the namespace by fully qualified name (e.g. wisp.string/replace) in order for it to work.
A lot of macros will fail to be accepted on the repl command line. I give this ns first (so you know everything has been imported)
And then I try something like:
Which looks like there's something else going on here with the way regular functions and macros interact - it seems like they're not finding each other?
I can replicate this with a much simpler example:
It's even replicable with the base functions