Arc Forumnew | comments | leaders | submitlogin
New version of import.arc (github.com)
1 point by Pauan 4853 days ago | 5 comments


1 point by Pauan 4853 days ago | link

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 4853 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.

-----

2 points by Pauan 4849 days ago | link

And even more fixes. It now works correctly enough that I have switched Arubic[1] over to use import.arc. This allowed me to safely make the changes I described here: http://arclanguage.org/item?id=15147

Now all you need to do is put (use arubic) at the top of your file and you'll be able to use nifty stuff like this:

  (map x foo ...)

  (mapfn prn foo)

  (import bar)

  (cons? foo)

  (isa foo 'cons 'table 'int)
Speed seems acceptable to me, though it is slower than using Arc directly (no namespaces, no Arubic, etc).

I've also been working on fexprs, writing a (much) better ssyntax parser, and a reader written in Arc. I plan for at least the ssyntax part to be included in Arubic, and probably the reader as well. I'm undecided about fexprs, mostly because of performance concerns.

---

* [1]: https://github.com/Pauan/ar/blob/lib/arubic.arc

-----

1 point by Pauan 4849 days ago | link

I just had a fun time tracking down what I thought was a bug, but actually wasn't. If you decide to use Arubic and then try to import something, you may end up with an error similar to this:

  > (import-as pprint "arc3.1/pprint.arc")
  Error: can't understand fn arg list 2
What's happening is that it encountered something like the following:

  (map (fn (e) (prn) (ppr e (+ col 2)))
       (nthcdr (1+ n) expr))
But keep in mind Arubic changes the meaning of "map", "all", "some", etc. which then causes the error. The correct thing to do then is to import it in Arc's namespace, rather than Arubic's namespace:

  > (w/arc3 (import-as pprint "arc3.1/pprint.arc"))
  nil

  > pprint
  #<namespace>

  > ppr
  Error: undefined variable: ppr

  > pprint!ppr
  #<fn>

  > (pprint!ppr '(1 2 3 4 5))
  (1 2 3 4 5)nil
I also have an idea for how I can get it to import correctly into Arubic's namespace, without the error.

-----

1 point by Pauan 4853 days ago | link

The reason I'm pushing for this is because I need it. With my work on Arubic (and other things), I've found myself needing namespaces more and more and more.

One example is that I changed it so [1 2 3] is expanded into (list 1 2 3) rather than creating an anonymous function. This works okay, until I start using Arc code/libraries that rely on the [] syntax, then things break.

Another example is that I want to change it so map, some, all, etc. are renamed into mapfn, somefn, allfn. And then there would be macros that would expand into those functions, like so:

  (each x foo
    (prn x))

  (eachfn prn x)


  (map x foo
    (+ x 5))

  (mapfn car foo)


  (some x foo
    (some x x
      (is x 5))

  (somefn whitec foo)
This removes 90%+ of the need for the [] syntax, which means I can then use it for something else, like `list`. But, once again, changing stuff like that breaks things. I could safely change it with proper namespaces.

And sorry, no, using ar's runtimes won't help, because they just create a new copy and then reload arc.arc into it; but the changes I'm describing break arc.arc itself. I need namespace inheritance, or something similar.

-----