Yes. At runtime. Even ignoring the inefficiency of expanding macros at runtime, there's one other thing that concerns me, and that's anaphoric macros.
If somebody writes a macro like "aif" or "aand" or whatever, you want the variable "it" to refer to the caller's namespace, but all the other variables to refer to the definer's namespace...
I think this is a solvable problem, but I think it'll require more complexity to solve.
By the way, I think I can add in a system where you can selectively choose which variables should be expanded in your namespace, and which ones shouldn't. Something like this:
(import foo)
(w/names (it)
(foo!something))
This says that the variable "it" should be expanded into your namespace, and all other variables should be expanded in foo's namespace.
Interestingly enough, if I implemented such a system, you could then overwrite certain variables in a macro's body:
(def helper (x)
(+ x 50))
(foo!something) -> 15 ; uses foo's version of helper
(w/names (helper) ; uses my version of helper
(foo!something)) -> 60
Also, `import` is still using `load`, rather than `use`. So you'd use something like this:
(import foo "../path/to/foo.arc")
Rather than this:
(import foo)
This has both advantages and disadvantages. What I'd like to do is have (import foo) behave like (use foo), but with namespace isolation. And then we could have an (import-as foo "../path/to/foo.arc") for more precise control.
---
I'd also like to note that "import.arc" is still basically alpha-state, so I make absolutely no guarantees about whether it'll work or not. My suggestion would be to try it, and if something breaks, tell me about it so I can try to fix it.
Basically, it's a prototype that attempts to demonstrate that the concept is both possible and feasible.
I think your discovery is important enough to warrant mentioning on the forum, since it is A) useful, and B) something that most people didn't know about. (At least, I knew about string->uninterned-symbol, but not procedure-rename).
Yeah... what 'latemac made me face up to is that my trick means that macros aren't transforming code to code any more. I'm not sure I know why, but I feel uneasy.
One question is can we come up with something better than making symbols qualified by namespace (such as how e.g. Clojure does it). It seems to me that making symbols namespace qualified is less convoluted than needing to use 'latemac with my function value trick, unless there's a problem I'm not realizing about namespace qualified symbols. What do you think?
"[...]unless there's a problem I'm not realizing about namespace qualified symbols. What do you think?"
Well, my opinion on namespaces is that they don't really fix anything if they're human-guessable prefixes; the prefixes themselves can collide, in which case I'd want the namespaces themselves to be in namespaces to resolve the collision. :-p That can be a vicious cycle (where do the namespace namespaces get resolved?), but there are a few decisive ways to resolve it:
- Name the namespace after a GUID or cryptographic signature, rather than something prone to human over-abbreviation.
- Name the namespace after an entry in some directory people already have to keep collision-free, like a file system path or a domain name.
- Don't plan on serializing or unserializing the namespace at all, and "name" it using something which has a unique identity at run time, such as an uninterned symbol.
At any rate, keep in mind that if all symbols are namespaced, then things like ssyntax will need to be able to handle that, and anaphoric variables will get either more complicated to make, more complicated to use, or both.
Doesn't have to be that complicated. I think it would be cool to let 'awhen be defined like this:
Here, 'mac and 'leak can be two macros which share information using an anaphoric variable of their own, say 'caller-namespace. They could expand to this:
Another thing to keep in mind is that just because symbols in different namespaces are distinct doesn't mean they need to refer to different variables. If I import 'foo, I'd like for the 'foo in my namespace and the 'foo in the library's namespace are both aliases for the same variable. That way I can just say (extend foo ...) to affect things in the library which use 'foo, instead of having to drill into the namespace using something like (extend the-ns/foo ...).
If I import 'foo, I'd like for the 'foo in my namespace and the 'foo in the library's namespace are both aliases for the same variable.
I like that.
leak!it
yes, there will need to be some mechanism to say that when my macro expands into a "bar", do I mean my bar or your bar? (Scheme's hygienic macros cleverly chooses which one you want most of the time, but makes it far too awkward to override the choice when it gets it wrong).
namespaces [...] don't really fix anything if they're human-guessable prefixes
How does the caller's symbols such as passed in through "test" end up in the caller's namespace? I suppose all symbols would need to be tagged with a namespace? For example, if I said:
(awhen x (prn x))
"x" would need to end up in my namespace after awhen was expanded.
Oh, I guess there's a couple of things I left out when I wrote that example.
To answer your question, the parameters to 'awhen would be wrapped up such that they were automatically associated with the place they came from. (Hmm, I guess that's a bit troublesome in itself, since it means they're essentially like Racket syntax objects, which need to be unwrapped to get at their original structure.)
More difficult to resolve: Somehow, the output of 'awhen oughta be wrapped up so that it's associated with the envioronment where 'awhen was defined. (And maybe this should be done more explicitly than it is in that example. Actually, while I was writing the example, I didn't remember this at all.)
In my current design for Penknife macros (toward the top of https://github.com/rocketnia/penknife/blob/1e5f3dd6cd86a2450..., a file in my Penknife draft[1]), both qq and leak are quasiquotes that anaphorically use lexical parameters of the macro call. They both use plainqq, and leak annotates the result so that it corresponds with the caller's namespace, while qq annotates it so it corresponds with the namespace surrounding the macro.
The hard part is that for qq to have a namespace to refer to, Penknife macros capture the local namespace they're defined in. That's not straightforward to translate to Racket; I basically end up making a shadowing namespace data structure at the start of each function call. On the other hand, it's probably sufficient to capture the global namespace. I was kinda just doing this for elegance's sake, since I kinda consider even a file-global namespace to be a local one, with imports as the local variables.
Got all that? I think I may have overexplained minutia this time around. ^^;
[1] There it is, my Penknife draft. :) I committed it to a branch a while ago, but I haven't really made a big deal out of that fact. Note that it doesn't work--it's not tested at all--and also note that the link's likely to break if and when I get back to it, finish it up, and treat it as the master branch.
On looking into the matter, it seems Racket is available on Windows and OS X, so that's basically taken care of. Hopefully its source compiles on non-x86{,_64} platforms as well, but I don't really develop for those.
I'd suggest just trying to compile it on whatever machine you might have in mind.
git clone http://git.racket-lang.org/plt.git
cd plt/src
./configure
make
make install # this builds executables and docs in place; takes a longish while
In Lathe/arc, I filter on the first argument's type using "ontype" or multiple first arguments' types using "ontypes":
(use-fromwds-as rc (+ lathe-dir* "orc/orc.arc"))
(= my (nspace))
; These are already defined in orc.arc, but I'm redefining them here
; for the sake of example.
(rc:ontypes rc.oiso2 (a b) (cons cons) my.cons
(and (rc.oiso2 car.a car.b) (rc.oiso2 cdr.a cdr.b)))
(rc:ontype rc.otestify () fn my.fn
self)
; General pattern:
;
; (rc:ontypes <rulebook> <parameters>
; (<type of a leading parameter> ...) <rule label (optional)>
; <implementation>)
;
; (rc:ontype <rulebook> <parameters other than the first>
; <type of the first parameter> <rule label (optional)>
; <implementation, where "self" is the first parameter>)
In orc.arc, this comes with an inheritance system I don't use much, but I've found I use 'ontype itself a lot when defining new types. Porting the inheritance system would be hard without porting Lathe's rule precedence system along with it.
In lathe.js, I recently tried adding lathe.instanceofRule( ... ), which defines rules that check the type of their first argument. This was largely a failure, as it clutters up the signature line so much that I end up spreading it over three lines of code, when often I could have had a one-line signature and a one-line explicit check. I haven't given up on it completely, though, since it decreases my token count slightly. :-p
Update: I've been mulling adding 'def :type' to wart for some time, so that I can say (def prn(x) :type 'integer ..) rather than (def prn(x) :case (isa x 'integer) ..).:
In my experience, it's usually the first argument, so I think having a :type would be useful, and in the cases where it isn't, you could fall back to :case (isa ...)