Now files only export the boxes they create. Which means...
; foo
(def foo ())
; bar
(import foo)
(def bar ())
; qux
(import bar)
(prn bar) ; works
(prn foo) ; error
Basically, "qux" imported "bar" which imported "foo", but "bar" only exported the boxes it defined, thus it didn't export "foo".
This is not only cleaner (and faster), but it opens up the possibility of having two separate Arc versions operating side by side, in the same namespace, able to communicate with each other.
That's important because I really want to have Arc/Nu use hygienic macros and hyper-static scope by default, but that would break Arc 3.1 compatibility. But now I can have Arc/Nu and Arc 3.1 operating at the same time. This was possible before, but was very difficult. Now it's easy.
After some thought, I decided to stop half-assing it. What I've been trying to do is make a new implementation of Arc that is backwards compatible with Arc 3.1 but adds new features and fixes bugs.
The problem is that eventually I'll run into features I want to add, or bugs I want to fix, and I won't be able to do that without breaking compat with Arc 3.1.
This is very restricting, and part of Arc is about unbridled freedom. I thought about going the pg route and just saying "screw compat!" but if I did that I'd just end up with Nulan, so what's the point?
So instead, here's what I'm gonna do. There will be two separate languages implemented by the Arc/Nu compiler: Arc/Nu and Arc/3.1.
Arc/3.1 is the Arc you know and love. Unhygienic macros, dynamically scoped globals, and most of the warts and bugs and missing features.
Arc/Nu is a language similar to Arc, but with new features, bug fixes, and general cleanup. It has hyper-static scope and hygienic macros. It doesn't try to maintain backwards compat with Arc 3.1.
Now, you might be wondering, what's the point? I mean, we already have vanilla Arc 3.1 as implemented by pg, so why should Arc/Nu support Arc 3.1 at all?
The twist is that thanks to the magical power of booooxes, I can have Arc/3.1 and Arc/Nu running in the same namespace. This means Arc/3.1 can import Arc/Nu stuff, and Arc/Nu can import Arc/3.1 stuff.
And it all works correctly, so that if you define an unhygienic macro in Arc/3.1 and import it into Arc/Nu, it will behave unhygienically. And vice versa, if you define a hygienic macro in Arc/Nu and import it into Arc/3.1, it will behave hygienically.
And you can do things like import an Arc/3.1 library and mutate its stuff, or do the same to an Arc/Nu library. There's no restrictions between the two, because it all runs in a single namespace.
This means you can write new code with all the shiny features, yet still use old Arc/3.1 libraries. And if you just plain like using Arc/3.1 but there's some Arc/Nu library you want to use? You can.
And if pg ever releases Arc 4, I can just add it as a third language, which will let it interact with both Arc/Nu and Arc/3.1. This is all very very easy to do thanks to boxes.
After some major refactoring, I reverted it back to use vanilla Arc 3.1.
Now, Arc/Nu has a rudimentary "multiple language" feature. When using the "arc" executable, you can now specify the "--lang" property.
For instance, when using "arc --lang foo", it will look for a folder called "foo" in the "lang" subdirectory. This folder should contain a file called "main" which should be written in Racket. Using this file, you can implement custom languages using the Arc/Nu compiler.
Currently, only two languages are supported: "arc/nu" and "arc/3.1"
As you can see, "nu.nu" is quite sparse at the moment, but it does load correctly if you use "arc --lang arc/nu". With some more refactoring, I'll be able to make it so that arc/nu and arc/3.1 can use libraries written in the other language.
Now multiple languages are fully supported. The way it works is that, within a file, you can use "w/lang" to temporarily change the language. For instance, suppose you had the following "arc/3.1" program:
(w/lang arc/nu
(var foo 1))
(= bar (+ foo 2))
Within the "w/lang" block, it's using "arc/nu", but outside, it's using "arc/3.1"! Using this, it's easy to import libraries written in "arc/nu":
(w/lang arc/nu
(import foo))
And vice versa, if you're writing a program in "arc/nu", you can use "w/lang" to import things from "arc/3.1":
(w/lang arc/3.1
(import foo))
By the way, because all of this is using boxes at compile-time, there's zero runtime cost. The only downside is that if you use two languages at once, it has to load both of them, which increases the startup time.
Well! I just learned something new! Racket's "procedure-rename" is ridiculously slow. In case you don't know what I'm talking about, it's just a function that lets you rename functions:
(procedure-rename (lambda () 1) 'foo)
Anyways, here's the timing tests I did for Arc/Nu using "procedure-rename":
What a ginormous difference. Removing it doubled the speed of Arc/Nu! Given that the sole purpose of "procedure-rename" is to, well, rename functions, I wouldn't expect it to have such a huge runtime performance penalty, but apparently it does...
Sounds like a good topic, and I'd be interested in hearing to hear your thoughts on this. I've been thinking about the same kind of thing, but I'm looking to use it in a weakly typed subset of a statically typed language. My motivation is mostly to see if we can make it convenient to externally manage the memory needs of otherwise encapsulated programs.
This is probably a discussion for another thread. ^_^;
I wonder if the compiler can't see through a 'procedure-rename call to realize it can still apply optimizations to the code. Like, maybe it can optimize ((lambda (x) ...) 2) but not ((procedure-rename (lambda (x) ...) 'foo) 2).
I use the server portion of Arc to write web-based applications, and would like to upload files.
Anarki hadn't worked very well or very fast when uploading. I might just bite the bullet and use web.py alongside Arc, and have Python calling Arc after a file has been saved. I might also resort to using Anarki, although breaking backward compatibility in a few ways can become a bit of a concern.
I wish there were a language that offered what I need.
Rewrote to use boxes. Faster. Cleaner. Has optional hyper-static scope and hygienic macros. Has a great namespace system based around w/include, w/exclude, w/rename, and w/prefix. Still completely compatible with Arc 3.1
A quick note if you're migrating old code to the new version: the "parameter" macro is gone. Use an ordinary variable and "w/" instead. In other words, rather than doing this:
(parameter foo ...)
(w/foo ...)
Do this:
(= foo ...)
(w/ foo ...)
Also, "=" still works like it does in Arc, but I recommend using "var" rather than "=", because it's hyper-static:
(var foo ...)
(w/ foo ...)
---
Another difference is that keyword arguments are no longer supported at all. This is because it's quite difficult to implement them properly, only a single function in Arc/Nu actually used them, and they're not backwards compatible with Arc 3.1, so I decided it wasn't worth it.
If you run into any other difficulties, feel free to post here and we can work it out.
Why didn't I just include it in Arc/Nu? Because I think the new system is better, so in the long run I really do want people to switch to the new system.