Arc Forumnew | comments | leaders | submitlogin
1 point by Pauan 4852 days ago | link | parent

I made quite a few major changes to get things to the point where I would consider using import.arc in my own code. In particular, I got macros working in a way that I'm satisfied with.

Same basic idea as before. You have a namespace, which contains mappings between variables/values, and also contains a reference to the parent namespace. When a variable isn't found, it checks the parent namespace, such forth and so on.

---

Biggest difference now is that I'm no longer using object.arc. Not because object.arc isn't useful, quite the contrary. Rewriting it to not use object.arc showed me just how useful of an abstraction it is. Using extend feels like using low-level byte code in comparison.

No, I changed it to not use object.arc because I plan for import to be a part of Arubic's core, so I need it to both be fast and have few dependencies. Then again, I might put objects in Arubic's core as well... so perhaps the dependency isn't so bad. I'm still undecided about that, though.

---

The second biggest difference is the way that macros are handled. Before, I simply evaluated the macro in the current namespace. I didn't like this, it felt hacky. In fact, I wanted to get rid of the call to eval completely, but of course you can't do that.

So instead, here's what I did. Macros now contain two bits of information: the function that does the code transformation, and the namespace the macro was created in. When executing the macro, it's always evaluated in the namespace it was created in, no matter what.

This will probably break all macros created before the change, though, as I can't make it retroactive. I can, however, put in code to guard against that, so it should be okay.

---

Another big change is how I actually represent namespaces. As said, I removed the dependency on object.arc, which means I now need to manually create a representation. I chose a flat list of namespaces: it will check each namespace in order until the variable is found, or it runs out of namespaces.

A flat list has many advantages, such as being quite fast, very little extra overhead, it's mutable so you can change the order of namespaces, etc.

Downside is it's clunky to implement, since I have no choice but to go in and extend multiple functions... in any case, it uses Racket namespaces now, rather than hash tables, so it should be faster, hopefully.

---

Oh yeah, another thing. With the previous version, if you didn't use w/namespace or similar, it wouldn't execute the namespace code, it just did whatever Racket did. But now it always executes the namespace code. One consequence is that before, if you tried to access an undefined variable, it would say "reference to undefined identifier: foo" but now it says "undefined variable: foo"

The only major consequence of this is speed: it'll probably run slower. Hopefully it should be fast enough for normal usage, though.

---

Lastly, I changed it so (import foo) will behave just like (use foo) except with namespace isolation. If you want to explicitly specify a path, you can use (import-as foo "/path/to/foo")



1 point by Pauan 4852 days ago | link

"When executing the macro, it's always evaluated in the namespace it was created in, no matter what."

I'll note that this basically makes macros pseudo-hygienic. Within a single namespace, they are unhygienic. But, if you import a macro from a different namespace, then they are hygienic. Thus, if people usually import code (rather than using "use" or "load"), then that minimizes the effect of macro hygiene.

-----