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

Okay, here's the remaining stuff. One thing we want to do is hide variables. To do this, we need a new primitive called "del". It works like this:

  (var foo 1)
  
  (def bar () foo)
  
  (del foo)
Now, if you try to use "foo", it will throw an error saying "foo is undefined". But if you call (bar) it will correctly return 1. So, using "del" doesn't really remove the variable, it just hides it. This can be used to control which variables your library exports.

---

Another thing that would be really nice is a way to get at the actual box for a variable. To do this, we need a new function called "get-variable-box" that accepts a symbol and returns a box.

There's a few things you can do with this. One of them is to create a "w/exclude" macro which lets you hide the variables you specify:

  (mac w/exclude (vars . body)
    `(do ,@body
         ,@(map (fn (x)
                  (if (bound x)
                      `(var ,x ,(get-variable-box x))
                      `(del ,x)))
                vars)))
And you use it like this:

  (w/exclude (qux corge)
    (load "foo.arc"))
This will import all the variables from "foo.arc" except "qux" and "corge". Of course it isn't limited to just importing. You can use it to write a library that has private variables:

  ; qux and corge are private: they can be seen inside this code block, but not outside
  (w/exclude (qux corge)
    (var qux ...)
    (var corge ...)
    ...)
Another thing this enables is hygienic macros, which I already explained (just change quasiquote to use "get-variable-box" rather than returning the symbol directly).

---

And we can define "w/rename" using "w/exclude":

  (mac w/rename (vars . body)
    (let p pair.vars
      `(w/exclude ,(map car p)
         ,@body
         ,@(map (fn ((x y))
                  `(var ,y ,x))
                p))))
---

We also want the ability to only import certain variables... but there's actually multiple ways to do this. You could have a primitive called "w/new-scope" that creates a new dynamic scope, similar to wrapping the expression in `(fn () ...)` except it works dynamically rather than lexically.

Another option would be a "w/new-namespace" primitive that returns an object that maps symbols to boxes. This is more flexible, but I'm not sure how fast it would be.

I'm going to go with the "w/new-scope" route, but if you have a better idea, I'm all ears. This is one part of Nulan that isn't quite fleshed out to my satisfaction yet. Also, I'm really not fond of using "eval" here.

  (mac w/include (vars . body)
    (let u (eval `(w/new-scope ,@body
                    (list ,@vars)))
      (map (fn (x y)
             `(var ,x ,y))
           vars
           u)))
Now if you say this:

  (w/include (qux corge)
    (load "foo.arc"))
It will only import "qux" and "corge" and nothing else. Just like "w/exclude", this isn't limited to just importing: you can use it to create private variables in libraries as well.


1 point by Pauan 4315 days ago | link

By the way, I think Arc also needs aliases. I know of at least two ways to make aliases: hard and soft. A hard alias is simply having two different symbols refer to the same box.

A soft alias is like a symlink: a new box that points to the other box. The way that I handled this in Nulan is to have &get and &set methods on each box.

These methods are expanded at compile-time like macros, but rather than expanding when the box is the first element of the list, it expands whenever the box isn't the first element of the list.

Translating it into Arc, it might look like this:

  (var foo 1)

  (defget bar ()
    `foo)

  (defset bar (x)
    `(= foo ,x))
Now whenever the compiler sees the box "bar" it will replace it with the box "foo", and when assigning to the box "bar", it will replace it with an assignment to the box "foo".

Nulan uses this not only to link variables together, but also for other purposes. For instance, assuming there was a function "get-cwd" and "set-cwd", you could do this:

  (defget cwd ()
    `(get-cwd))

  (defset cwd (x)
    `(set-cwd ,x))
And now `cwd` would expand to `(get-cwd)` and `(= cwd 1)` would expand to `(set-cwd 1)`.

The reason I mention this here is that the namespace macros (w/exclude, w/rename, and w/include) should probably use hard aliases rather than using "var".

-----