Arc Forumnew | comments | leaders | submitlogin
2 points by Pauan 4268 days ago | link | parent

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.



2 points by Pauan 4267 days ago | link

https://github.com/Pauan/ar/commit/2c7c5f8f0ea2f313a47fadb2a...

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"

https://github.com/Pauan/ar/tree/2c7c5f8f0ea2f313a47fadb2ae6...

https://github.com/Pauan/ar/tree/2c7c5f8f0ea2f313a47fadb2ae6...

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.

-----

1 point by Pauan 4266 days ago | link

https://github.com/Pauan/ar/commit/6352cbffc8866fac7b19ed3b3...

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.

-----

4 points by Pauan 4266 days ago | link

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":

  > (+ 1 2)
  Arc/Nu   iter: 45,419,082   gc:   0   diff:  0.00%
  Arc 3.1  iter: 52,867,613   gc: 188   diff: 16.40%

  > (no ())
  Arc/Nu   iter: 26,594,507   gc:   0   diff:   0.00%
  Arc 3.1  iter: 54,532,505   gc: 152   diff: 105.05%
And here's the results when I removed "procedure-rename":

  > (+ 1 2)
  Arc/Nu   iter: 88,933,497   gc:   0   diff: 71.88%
  Arc 3.1  iter: 51,741,236   gc: 116   diff:  0.00%

  > (no ())
  Arc/Nu   iter: 78,026,418   gc:   0   diff: 37.11%
  Arc 3.1  iter: 56,909,869   gc: 156   diff:  0.00%
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...

-----

3 points by lark 4265 days ago | link

I sometimes wonder if garbage collection in dynamic languages is necessary.

-----

2 points by rocketnia 4265 days ago | link

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. ^_^;

-----

1 point by akkartik 4265 days ago | link

My arc variant uses ref-counting, for what it's worth: https://github.com/akkartik/wart/blob/adf058706b/010memory

-----

1 point by rocketnia 4266 days ago | link

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

-----