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

Well, after a couple false starts, I finally got a Nulan interpreter written in Racket working. Oh, and, ignore the README, it's super duper outdated and I haven't updated it yet.

You can use "./nulan" to get to a REPL. Near the bottom of "01 nulan.rkt" you can see all the stuff that's currently supported:

https://github.com/Pauan/nulan/blob/f716b592609f9cb551a3ddc0...

Right now, that means:

* Objects! Hurray!

* Vaus! Hurray!

* Immutability! Hurray!

* Lexical global variables! Hurray!

* An awful lot of functionality is customizable via objects. That includes custom pattern matching. I plan to make more things customizable as time goes on.

---

I found that writing it as an interpreter was an awful lot easier than trying to write it as a compiler, because I discovered that the way I was doing pattern matching made it impossible to determine which variables in a vau are free.



1 point by Pauan 4439 days ago | link

Oh yeah, I mentioned pattern matching is customizable... it's actually really really awesome. First, you've got this simple function "pattern-match":

https://github.com/Pauan/nulan/blob/f3f5c3550273bd757ca2d7f3...

How it works is... it's passed three arguments: the environment, the pattern, and the value. It returns a new environment which is the result of matching the pattern to the value.

But it doesn't do an awful lot: it handles symbols, the wildcard ~, lists use the %pattern-match function of an object, and everything else just uses "eq?".

So, for instance, the "list" pattern matching is implemented here:

https://github.com/Pauan/nulan/blob/f3f5c3550273bd757ca2d7f3...

It's actually a lot simpler than it looks. It just recurses down the pattern and value, calling "pattern-match" on each element of the lists. Then, at the end, if either the pattern or value is not null, there was a mismatch, so it throws an error.

The argument list of a vau obviously uses the "pattern-match" function, but another place it's used is "def":

https://github.com/Pauan/nulan/blob/f3f5c3550273bd757ca2d7f3...

See how simple that is? It simply unboxes the dynamic environment, runs it through the pattern matcher, then assigns it to the dynamic environment[1].

With that small amount of code, the following now works:

  (def (list a b) (list 1 2))
Which will bind "a" to 1 and "b" to 2. And as said, it's completely customizable: just slap a %pattern-match property onto any object.

And because Nulan is a vau-based language with first-class environments, you can use "def" inside vaus to create local bindings:

  (fn ()
    (def a 5)
    a)
This is like "var" in JavaScript only much better.

---

* [1]: The reason the Racket version is a bit funky is because the "pattern-match" function is written in Nulan style. Here's how "def" would be written in Nulan:

  (def def
    (vau e {n v}
      (let v (eval e v)
        (set! e (pattern-match (unbox e) n v))
        v)))

-----

1 point by Pauan 4439 days ago | link

I just added in dictionary pattern matching:

https://github.com/Pauan/nulan/blob/95a73a115fdef0cb27fae2e7...

See how easy that is? It took me maybe 5-10 minutes to add it in.

And it naturally supports recursion as deeply nested as you want:

  (def (dict "foo" (dict "bar" b) "qux" c)
       (dict "foo" (dict "bar" 1) "qux" 2))
Now the variables "b" is 1 and "c" is 2.

-----

2 points by kinleyd 4439 days ago | link

Well done, Pauan. I haven't yet tried Nulan, but plan to take it for a spin in the days ahead.

-----