Arc Forumnew | comments | leaders | submitlogin
Arubic-style import, now as a library on my fork (github.com)
2 points by Pauan 4957 days ago | 14 comments


1 point by Pauan 4957 days ago | link

Because it uses "lib/object.arc", import will of course only work on my fork, but I plan to change that soon, so it'll work on normal ar as well.

I'll also note that it's missing some features, and is a bit broken in some ways, but I have a plan to fix that. I also noticed that it seems to be a bit slower (than using load), but that could be because I'm using a hash table rather than a Racket namespace. I'll switch over to Racket namespaces later and see if it helps any.

However, for basic stuff, it works. As an example, let's suppose you wanted to load up "arc3.1/pprint.arc", which creates a bunch of functions, like ppr. But now let's also suppose that some of those functions clash with other functions in your namespace... if you just use `load`, you'll get a namespace collision and stuff will break. Import to the rescue!

  >>> ppr
  error: reference to undefined identifier: ppr

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

  >>> pprint!ppr
  #<procedure>

  >>> (pprint!ppr "foobar")
  "foobar"nil

  >>> ppr
  error: reference to undefined identifier: ppr
What import does is create a new namespace, then load everything inside that namespace. You'll note that it did not pollute the current namespace, since ppr is still undefined even after calling import.

But you can still call functions in the pprint namespace by using table notation. There are some neat things you can do with this. Aside from the obvious one of loading libraries into separate namespaces so they don't collide, you could load the same library twice, or you could load multiple libraries into the same namespace, while still keeping it separate from your namespace:

  (= foo (new-namespace))

  (w/namespace foo
    (load "lib/bar.arc")
    (load "lib/qux.arc"))
Now the namespace foo contains both bar.arc and qux.arc, but neither of them have polluted your namespace.

By the way, in addition to namespaces, it actually supports anything with type 'table, so the following works:

  >>> (w/namespace (table)
        (eval '(+ 10 5)))
  undefined variable: +

  >>> (w/namespace (obj + +)
        (eval '(+ 10 5)))
  15
And calling (eval ... foo) is a shortcut for (w/namespace foo (eval ...))

  >>> (eval '(+ 10 5) (obj + +))
  15
When you call `new-namespace`, it defaults to the current namespace (I plan to change that), but you can pass in an argument to use a custom namespace:

  (= foo (new-namespace (table)))
You can think of new-namespace as creating a "proxy" or a "clone" of an existing object. If you understand JavaScript prototypes, it's exactly like that. This allows you to load stuff into a namespace without it clobbering anything else.

-----

1 point by Pauan 4957 days ago | link

I would just like to point out that this is only possible (as a library) because ar is so damn hackable. Big thanks to aw for making ar.

---

I'd also like to note something here. Because you can create custom namespaces, there is the theoretical ability to create a "safe namespace" that would not be able to access things like "system" or similar destructive things.

Then, if you ever wanted to load up a library, rather than inspecting it to make sure it doesn't do anything bad, you could just load it into the safe namespace. This won't protect against things like infinite loops, or programs designed to consume a lot of CPU/RAM, but it will protect against the obvious stuff like (system "rm -rf /") and such.

I haven't added in support for that just yet, but it is planned.

---

Also, __built-ins* is a terrible name, and I plan to change it. :P

-----

2 points by aw 4957 days ago | link

"ar is so damn hackable"

heh, can I quote you on that? :)

-----

1 point by Pauan 4957 days ago | link

Yes. And it'll be even more true when ar gets defcall. I mean, really, implementing dynamic variables, optional args/destructuring, ssyntax, objects, and namespaces/import as plain-old Arc code is impressive. Very impressive. In fact, I don't know of any other language that can do that, though I've heard Smalltalk is very flexible.

Since most of the Arc compiler is actually in the Arc namespace (combined with having access to Racket), I get the feeling that you could do almost anything with ar, which is most definitely not true of Arc/3.1.

-----

1 point by aw 4956 days ago | link

Not sure if using an implicit variable for specifying the namespace is the right choice here... I find implicit variables most useful when there's a single value that needs to passed through multiple layers of function calls. For example, in the Arc web server, I'm only responding to a single request at a time, so I find it useful for the request to be an implicit value.

When I have more than one value that I'm working with at a time, then I find implicit variables often become awkward, and I'd prefer to just specify the value explicitly.

It seems like namespaces may fall into this latter category, since I'll be working with my namespace and the other namespace, and perhaps more than one other namespace.

I could be wrong of course, and there's a good example of when passing the namespace implicitly is simpler than the alternative.

By the way, I've been calling the value holding the global variables and other attributes of a language instance a "runtime", which happens to be implemented with a Racket "namespace". I think "namespace" may be too narrow a description since a runtime may have other configuration settings (such as how it handles optimizations) besides just which global variables it has. Thus I'd name things "new-runtime", "w/runtime" etc.

-----

1 point by rocketnia 4956 days ago | link

I feel like having an implicit variable for the namespace is a bit of a low-level approach, since the core Racket namespace tools default to the dynamic (current-namespace) already. That's also an intuitive default for them to have, so I don't see anything wrong with Arc code doing it the same way. Whether or not it's beneficial in an in-the-large way, at least it's concise.

Anyway, how about this for "multiple layers of function calls": If you call 'load, that calls 'eval. Two whole layers! ^_^

---

"I think "namespace" may be too narrow a description since a runtime may have other configuration settings (such as how it handles optimizations) besides just which global variables it has."

I agree. I think "runtime" is a pretty good name for what you're headed for. :)

-----

1 point by Pauan 4956 days ago | link

"I feel like having an implicit variable for the namespace is a bit of a low-level approach, since the core Racket namespace tools default to the dynamic (current-namespace) already."

Yeah, I have nothing against making macros or functions that handle namespaces in other ways, but I'd rather have a simple and concise low-level way too.

---

"I agree. I think "runtime" is a pretty good name for what you're headed for. :)"

Yeah, but I think the word "namespace" makes perfect sense for my library, especially considering that a namespace is a simple mapping between names and values. You can even pass in tables, so I think "namespace" is just fine.

Of course, in ar itself, using "runtime" is also perfectly fine, because ar namespaces might contain things other than a simple mapping, so I don't see a problem with ar using "runtime" and my library using "namespace".

-----

1 point by Pauan 4956 days ago | link

"I find implicit variables most useful when there's a single value that needs to passed through multiple layers of function calls."

I'm finding a lot of uses for implicit variables. I've come to like them a lot. :P I'll note that there can only be one namespace active per executing code, so it made a lot of sense for me to use implicit variables.

---

"When I have more than one value that I'm working with at a time, then I find implicit variables often become awkward, and I'd prefer to just specify the value explicitly."

You mean like this?

  (w/namespace foo ... do stuff ...)

-----

2 points by jsgrahamus 4956 days ago | link

Getting 404 on link

-----

2 points by rocketnia 4956 days ago | link

Guess Pauan broke the link by replacing the master branch with one that could be pull-requested back into awwx/ar. Here's import.arc on the "old" branch: https://github.com/Pauan/ar/blob/old/lib/import.arc

By the way, Pauan, how did you manage to make that switch? Your GitHub history has an entry that says "master is now 2be9ac7", and your pull commit worked out better than mine did. I'd like to know your secret. ^_^

-----

2 points by Pauan 4956 days ago | link

Yeah, sorry, that was intentional, but I didn't realize the links would break. In hindsight, I should have.

Well, it's pretty simple[1], really. I used git fetch upstream to grab all the changes from ar. Then I used git reset --hard to reset my master branch to ar's branch, completely destroying everything on my branch, including commits.

Then I used git push -f to force my changes onto GitHub. Voila, now my fork is in the exact same state as ar. Of course, before I did all that, I created the old branch, so I wouldn't lose my work. Here's roughly what I did:

  git branch old
  git push origin old
  ... do stuff ...
  git fetch upstream
  git reset --hard 2be9ac75f67edfe9b3c43a9515dd78acebba6f1c
  git push -f origin
At least, I think that's what I did. I actually had to experiment and mess up some stuff before I figured out how to do it. So my directions may be wrong and you may bork your repo!

By the way, while I was trying to do that, I found this: http://stackoverflow.com/questions/772846/create-git-branch-...

But their directions are somewhat different from mine. I don't think I needed git pull for instance, and git push origin :master didn't work for me. So I had to use -f to force the push, since git was kindly telling me that it might destroy my commits (which I wanted to do).

---

After using git for a while, I gotta say, I like it a lot more than Mercurial. git gives you the power to do stuff, whereas Mercurial seems to hold your hand a lot more, and say, "no I can't let you do that, Dave"

Now, I'm not saying Mercurial is bad, just that I like the raw power of git, in the same way that I like the raw power of Arc. So for me personally, git seems to be the winner.

---

* [1]: s/simple/obtuse black magic/

-----

2 points by rocketnia 4956 days ago | link

Thank you so much. ^_^ Between that, http://book.git-scm.com/1_the_git_object_model.html, and http://blog.nelhage.com/2010/01/git-in-pictures/, Git's starting to make more sense.

It's too bad the links break. But wait, the pull requests' links don't break, because they link directly to the blobs.... I bet the blobs will stick around as long as at least some branch points to them, so playing fast and loose with lots of branch-renaming is only going to hurt tip-of-branch links. Too bad GitHub doesn't have some way to specify redirects (or at least I think it doesn't).

-----

2 points by akkartik 4956 days ago | link

I know that git lets you rename branches. And apparently github lets you configure the default branch as well. http://support.github.com/discussions/repos/5390-how-to-rena...

-----

1 point by Pauan 4956 days ago | link

Also, I'm not actually sure how well my old branch actually works, since I've moved onto working on ar. I do plan to port import.arc so it works on ar, since ar now has defcall (yay).

-----