A Few Nu Details Emerge
I think that experienced programmers will find that Nu is surprisingly simple, mainly because there’s so little that’s really new about it. I reused a lot of existing Objective-C objects and idioms and drew nearly everything else from Lisp and Ruby. To me, Nu is as much a cherry-picked set of idioms as it is a language.
Here are some details.
Maybe it’s just because I wrote it, but I really think that Nu’s architecture is quite transparent: S-expressions are parsed into self-evaluating objects, all of which are implemented with classes expressed in the Objective-C runtime.
Every object has an eval: method that triggers its own evaluation in a passed-in context, and contexts are represented by NSDictionaries of key-value pairs. Context keys are symbols, symbols are “interned” strings stored in a symbol table, and each symbol also has a slot for a globally-assignable value.
One of the most important new elements of Nu is the NuCell class, an Objective-C version of the classical Lisp cons cell. There’s the first strike against Nu: making cells Objective-C objects wastes a word per cell (for the isa pointer), but to me, the simplicity of that was well worth the storage cost. All Nu code is parsed into structures built out of cells, and since cells and symbols both support archiving, the “compiled” code can be written to a file or sent over a network connection.
Objects of most preexisting ObjC classes just evaluate to themselves, but NuCells (a.k.a. lists) respond in a special way.
A list that begins with an operator or block sends that operator or block an “eval” method with the rest of the list as arguments. Here’s a simple example:
(+ 2 2)
+ is a NuSymbol that is bound to a singleton operator object that implements addition. The two numbers are NSNumbers, and everything is linked together with NuCells.
Lists that begin with other types of objects use the rest of the list to construct a message that is sent to the object at the head. Here’s how you might use that to create a Cocoa window:
((NSWindow alloc)
initWithContentRect:'(30 200 800 800)
styleMask:(+ NSTitledWindowMask
NSClosableWindowMask
NSMiniaturizableWindowMask
NSResizableWindowMask)
backing:NSBackingStoreBuffered
defer:NO)
Operators and blocks are objects, and occasionally we want to send messages to them. Since the normal calling convention doesn’t allow that (lists that begin with an operator or block are treated as operator calls), there’s a special operator that you can use to explicitly send messages to objects. Assuming that block is bound to a block, (send block arguments) is the Nu equivalent of [block arguments]; in Objective-C.
In both operator calls and message sends, the evaluation context is sent with the evaluation message so that arguments can be evaluated in the caller’s context. Arguments to methods are always evaluated, but operators may or may not evaluate all of their arguments, and sometimes operators evaluate their arguments multiple times. For blocks, there are a couple of interesting cases.
A block is represented by a list of argument names, an execution context (saved when the block is created), and the parsed code to be evaluated. The do operator can be used to explicitly create blocks; several other operators create blocks implicitly.
When a block is created with the function operator or called anonymously, all of its arguments are evaluated in the calling context before the block is evaluated.
When a block is added as a method, it is wrapped in a libffi closure that automatically adds self and super to the block’s context each time it is evaluated. Method arguments are always evaluated in the calling context before the method block is evaluated.
Most of the time, the evaluation of code in a block takes place in the context that was saved when the block was created. But when a block is created with the macro operator, no context is kept. Instead, when the macro is evaluated, all block evaluation takes place in the caller’s context. To access the macro’s arguments, a special symbol margs is pushed into that context (and if there’s an existing value, that’s saved until the macro finishes). The margs symbol is just the list of unevaluated arguments to the macro; the macro itself determines when and how they are evaluated. Next, any names in the block that begin with a double underscore are replaced with generated symbols, and then the block is evaluated. Here’s a simple example:
(macro factorial
(set __x (eval (car margs)))
(if (== __x 0)
(then 1)
(else (* (factorial (- __x 1)) __x))))
In practice, you’d never compute factorials like this, but it illustrates an important problem that can occur with macros. Since macros are evaluated in the caller’s context, each recursive evaluation of factorial potentially sets our double-underscored x to a new value. That would be bad, so each time we evaluate this macro in Nu, the double-underscored x is replaced with a new automatically-generated name that is guaranteed to be unique.
Wow, describing this is more work than I expected. Let’s take a break. Send me some feedback and I’ll clean this up, and maybe our next topic will be Nu’s interaction with the Objective-C runtime.
Comments (8) post a reply
Hi, this is really cool! Writing documentation is a lot of work, good luck…
Just a quick typo: you have one extra parenthesis at the end in your code here:
((NSWindow alloc).... defer:NO))
To put things into context, could you write an overview of the Nu language? You seem to be writing the internals manual, which is great, but doesn’t help me understand why I’d want to use this over plain Objective-C, and, therefore, why I should be interested.
Otherwise, a lot of this is not going to make much sense to me personally. I’m familiar with Lisp, with some Lisp implementations, with ObjC, and with the ObjC runtime, and with Ruby and the Ruby implementation, so it’s not that I don’t understand what you’ve written.
I guess the primary question I have is: what does Nu add to ObjC besides the inherent Lispiness? It sounds like it’s just an Objective-C-focused Lisp variant. There is virtue there, but is it the sole virtue?
In other words, “what is it for?”
Now you get to throw me out of the classroom. :)
Charles: thanks for catching that. Fixed.
Chris: I guess since I don’t expect to be selling Nu, I’m not so oriented towards advocacy (and my blog server couldn’t take that much traffic, anyway). Nu is reflective, interpreted, and expressive, and Objective-C isn’t (very). I like Objective-C as a platform, but wouldn’t want to write everything in it any more than I’d write in x86 assembly. I couldn’t find a high-level language that worked well with Objective-C and did everything that I wanted (although F-Script is very cool), so I wrote Nu.
As a follow-up on Chris question, here is mine: one thing which is powerful with embedded interpreted language is the possibility of testing things quickly without recompiles (this is not the only use of this kind of bridge, but one that I like).
For this to work, there are 4 constraints (for me, at least): (1) the syntax should be similar to ObjC so my brain does not have to switch constantly from 2 very different syntaxes, (2) the “philosophy” of the bridged language should be close to ObjC (this is slightly different from 1, and is probably also highly dependent on the internals), (3) easy to setup a call to the interpreted language from within the ObjC code, (4) the language used in the bridge should have some benefits compared to ObjC.
For instance, the Java bridge would have relatively poor scores on 1, 2 and 4, and OK score for 3.
Any interpreted language would already have a good score for 4, as already it removes a compilation step.
Ruby would have good scores for 1 and 2, with some issues that you have struggled with when working on the RubyCocoa and RubyObjC bridges. Ruby would have an excellent score with 4, and I don’t know about the score for 3.
A Javascript bridge (when/if it becomes less private that it is now, buried inside Webkit) would have good scores for 2, 3 and 4, and an OK score for 1.
Now, what gets me interested in Nu is that scores would be high for all 1, 2, 3 and 4.
Does that make sense?
Charles,
I agree and think you make a good argument for Nu. In a previous post titled “Finding the Software Industrial Revolution”, I looked back at a 1990 paper by Brad Cox that suggested two key enablers for a revolution in software technology. The first was assembly technology, and Objective-C is good for that. But Objective-C isn’t particularly helpful for the second requirement, a specification technology, because it is compiled and not very expressive. Nu is aimed straight at that and builds on decades of accumulated experiences with dynamic languages, but without the arrogance. In other words, Nu isn’t a Lisp that demands that you write in Lisp, it’s a Lisp whose purpose is to help you write high quality Objective-C components and do your gluing with Nu.
Why all the secrecy? Is this an open source project or something closed source or commercial? If it is or will be open source then why not dump the code on for example Google Code now and let others look at it?
Stefan,
Maybe it’s my own pickiness; I have some organizational and technical issues to work out before I will be ready to release code.