Tuesday, April 25, 2006

Characters

Quick: which character is '\u03b5'?

Let's see, well, since it's between 0370 and 03ff it's clearly Coptic or Greek. The Greek lower-case characters start at 03b1 so it would be the lower case form of the fifth Greek character. That would be, let's see, ε! Man, that Unicode stuff is just so logical!

I don't think there's anything wrong with Unicode. But as soon as people have to use the character codes directly, for instance when using Unicode character constants in languages like Java and C#, there's trouble. There are tens of thousands of Unicode code points and I can't even remember which ASCII character is line feed and which one is carriage return. On the rare occasions where I either read or write code that uses Unicode constants or ASCII control characters, I usually have to open a browser look up the values myself. That sucks.

The fortress language improves on things: there, instead of writing the code of a character in an identifier, you can write the name. It just seems so obvious really: the Unicode standard defines a name for all the characters so why should I have the trouble of looking up the character code?

In neptune you're welcome to still use character codes to specify Unicode characters. Writing '\u03b5' means greek small letter epsilon just as it does in Java and C#. But inspired by fortress we've added another syntax for specifying Unicode characters by name: writing \<name> specifies the Unicode character called name. So instead of writing '\u03b5' you can simply name the character: '\<greek small letter epsilon>'. If you feel that 'x' is too straightforward you can specify it equivalently as '\<latin small letter x>'. This works both in character literals and text strings:
"From \<greek capital letter alpha> to \<greek capital letter omega>"
which means the same as, but is a lot easier to understand than
"From \u0391 to \u03A9"
Besides Unicode names, we also allow the ASCII control characters to be specified by their short and long names. This means that I don't have to remember that line feed is 10, not 13; instead I can just write '\<line feed>' or '\<lf>'.

The first time you want to write the character ε you probably still have to look it up to see that it's called greek small letter epsilon. But there's a lot more logic to the name than to the raw character code and there's a better chance you'll remember next time. And it will of course be obvious to anyone reading the code which character it is. The only problem I see is that the names tend to be very long. Fortress allows you to use shorter forms of some characters: you can for instance leave out words like "letter" in character names. If the length of the names turns out to be a problem we might add something like that at some point.

Either way, I think this is a pretty nifty feature. And I wouldn't be surprised if it turns out that there are other languages that have similar mechanisms.

Tuesday, April 18, 2006

Why Neptune?

Lately I've been writing a lot about the neptune language, the replacement for smalltalk on the OSVM platform. I've tried to focus on the technical aspects both because there's lots to write about but also because when you're blogging about work, keeping it technical makes it less likely that you'll accidentally write something, shall we say, career limiting. In this post I'll take my chances and write about one of the less technical aspects: why we made the switch from smalltalk to a different language, and why we decided not to use an existing language but design our own. It is not about the strategic business decision of changing the language -- for that you'll have to go through the official channels -- but my perspective on the switch as one of the people developing the platform.

I'll start off by explaining a bit about OSVM and why we used smalltalk for the platform in to first place.

Why Smalltalk?

To give some context I'll start of by describing what OSVM is. The OSVM platform is basically a virtual machine targeted at memory-constrained embedded devices. The platform has been designed with several goals in mind. It should be very simple in order to make the runtime as small as possible, since memory is limited. It should never be necessary to restart the system -- if you're dealing with software for applications security or medical equipment you want the system to be able to run without any interruptions, ever. If an error occurs, and of course they do, it shouldn't kill the system; rather, it must be possible to diagnose and fix the problem in the field. That's not always possible but there's a lot you can do to make it more likely that the system will survive an error. Finally, it must be possible to update the running program in the field, both to fix errors and to install software upgrades. Being able to diagnose a bug in your program is no good if you can't update the software to fix the problem. This, in short, is what OSVM is all about.

These requirements didn't pop out of thin air. When you're used to working with smalltalk this is what you expect from your platform: robustness, flexibility and the ability to interact directly with running programs and to update code on the fly. The smalltalk language and environment were developed together, which means that language was designed with all the issues that are important to us in mind. This is not the case with any language I know outside the smalltalk family, which makes it uniquely suitable for our use.

But using smalltalk is not without problems.

Why not smalltalk?

The languages most used in writing embedded software is C and C++. If a company considers using a platform like OSVM instead they not only have to consider the direct cost of buying the system from Esmertec. They also have to consider the indirect cost in time and money to train their employees to use the system, before they get actually writing software.

C++ is a object-oriented programming language (let's be generous and call it that) but other than that, C++ and smalltalk are completely different. In order to use smalltalk, a C++ programmer would have to learn everything from scratch: how to declare a variable, how do write an if and while statement, how to write a string or character literal, everything. Since most embedded software is developed in C and C++, that means that there is a considerable indirect cost associated with using smalltalk with OSVM.

The pragmatic programmers recommend that you learn a new language every year. I think learning new languages, both programming and natural, is a great hobby. If I have a deadline, however, "broadening my thinking" by learning a new programming language is not my top priority. Imagine that you were writing a book and had the choice to either write it in English using a primitive ASCII text editor, or in Danish using a state-of-the-art graphical text editor. Assuming that you don't speak Danish, what would you choose? Or, perhaps more realistically, what would your boss choose for you?

Even though the OSVM system is very much inspired by smalltalk and the smalltalk environment, what is important is not the language itself. The important thing is the properties of the language: robustness, flexibility, serviceability, etc. The goal of the neptune language is to retain all the important aspects of smalltalk but to adapt the less important ones, like the syntax, to make it easier to learn and use by programmers who are used to more mainstream programming languages. That is not to say that syntax is not an important part of smalltalk but it is not essential for its use as a part of OSVM.

I may give the impression that I think smalltalk is the pinnacle of programming language design and any change is a change for the bad. That is not the case: I think there are plenty of ways to improve smalltalk. In changing the language we've tried to address many of the quirks and shortcomings of plain smalltalk to get a system that is all-round better to use and not just easier to learn for C or C++ programmers. I won't go into any details about how I think neptune improves upon smalltalk, though, since that's a touchy subject that I don't want to obscure the point I'm trying to make here.

Why Neptune?

Why design a whole new language instead of using an existing one? The whole point is to not force people to learn a new programming language and if we design a new one we can be absolutely certain that no one knows it.

It all comes down the nature of the OSVM platform. The language has to be very simple since the runtime must be minimal. This requirement alone excludes many languages including Java, C# and python. But the real killer is that it must be possible to update the code dynamically. I don't know of any languages (except smalltalk) where you can give a reasonable meaning to updating the code of a running program in all cases. This is especially bad in languages where the runtime behavior of a program depends on static typing. When designing a language it is very easy to make decisions that make it very hard to update code on the fly. You have to keep this kind of use in mind during the design and smalltalk is really the only language designed like that. Having decided that smalltalk is not the way to go, that means that there are really no existing languages for us to use. Hence neptune.

Designing the language from scratch also means that we're free to adapt the language to fit our use. The result is a language that, beneath an added layer of syntax, is still very close to smalltalk, and retains many of the properties that makes smalltalk unique. An indication of how close smalltalk and neptune are is that the virtual machine is almost exactly the same as before, the biggest change being that indexing is now 0-based instead of being 1-based. On the surface, however, neptune is much closer to C++ or Java. Basic things like variable declarations, control structures, operators and literals will all be familiar to a C++ or Java programmer. When learning the language this means people will be able to use much of what they already know and will be able to be productive almost immediately, and to move on more quickly to the more advanced concepts such as traits, blocks and optional types.

Some might consider it to be "dumbing" the language down but we've also made a conscious effort to make it possible for people to use the language without necessarily being familiar with the full generality of it. When you're using an ifTrue:ifFalse: expression in smalltalk it might be intellectually gratifying to know that it's not a "magic" construct, just a method call with two block arguments. But it's not something you necessarily want people to understand from day one in order to use the language. In neptune, we have a separate if statement but that is still just a shorthand for a call to the if method. You're free to call that method directly, without the special syntax, to the exact same effect. The full generality of smalltalk, along with some new constructs, is still available to you in neptune but instead of having it forced upon you, you can grow into it gradually. Even though it make the language a bit less clean than smalltalk, I think that is a pretty reasonable tradeoff.

Saturday, April 15, 2006

Traits

One of the changes we've made in going from smalltalk to neptune, beyond pure syntax, is in the object model. We've added an optional type system and introduced two new concepts: traits and protocols. I don't mean new in the sense that we invented them, they're pretty well known actually, but they're new in the sense that they're not part of standard smalltalk. In this post I'll give a brief introduction to traits.

Smalltalk's single inheritance model is clean and simple, but it also has many limitations. The most obvious limitation is that a class can only share code with one other class, its superclass. In addition, there are also more conceptual limitations. If I create a subclass B of class A, an instance of B should be considered a special case of an instance of A. The typical toy example of this is animals. Let's say that the class Eagle extends the class Bird which extends the class Animal. This models the fact that an eagle is a bird and a bird is an animal. On the other hand, a bat shares many of the characteristics of a bird but it is not a bird so the class Bat cannot inherit from Bird. Even though there is much code in the class Bird that could be used in class Bat, single inheritance doesn't allow them to share code because it is just conceptually wrong. I guess this is a pretty poor example but at least if someone disagrees and believes a bat can be implemented by using parts of birds I can just sit back and wait for the villagers with torches and pitchforks to take care of him. But I digress.

C++, another ancestor of neptune, has multiple inheritance which at least solves some of the problem -- a class can now share code with more than one other class -- but introduces so many other problems that we've never really considered it as a serious alternative. The solution we've decided to go for is traits. Traits seem to be slowly making their way into the mainstream as a more flexible way of sharing code between classes; variants can be found in languages such as perl6, scala and fortress.

To explain how traits work in neptune I'll show you an example from our libraries. One place where we've used traits in our libraries is for implementing relational operators such as <, >=, etc. In most languages I know, the standard way of making an object x comparable to other objects is to add a method to x that takes the object to compare it with, y, and returns an integer signifying whether x is less than, equal to, or greater than y. In java that method is compareTo: if x is less than y, x.compareTo(y) returns a negative number, if they're equal it returns zero and if x is greater than y it returns a positive number. In neptune, we use the spaceship operator, <=>, for comparing objects. For instance,
"aardvark" <=> "zebra"
returns a negative number because the string "aardvark" is lexicographically less than "zebra". That's all nice and general but it is not very convenient or readable. Writing
if ((x <=> y) < 0) return y;
is a lot less clear than writing
if (x < y) return y;
This is where traits come in. Since the relational operators are completely defined by the comparison operator I can implement the relational operators as a trait:
trait TRelation {

require int operator <=>(var that);

bool operator ==(var that) {
return (this <=> that) == 0;
}

bool operator <(var that) {
return (this <=> that) < 0;
}

// ... and >=, <=, !=, > ...

}
This doesn't define an a class but a set of methods that can be used by any class, provided that is has the <=> operator. One example of this could be an implementation of fractions:
class Fraction {

use TRelation;

int num, denom;

bool operator <=>(var that) {
// ... fraction comparison ...
}

...

}
The easiest way to think of the use declaration is that it essentially copies the methods declared in the TRelation trait and pastes them into the Fraction class. The only requirement in this case is that class using the trait must define the comparison operator. By the way, a class can use as many traits as it wants.

Unlike inheritance, the class Fraction is not put in any kind of "relationship" with the trait TRelation. Traits are nothing more than collections of methods that you can import into your own classes almost as if you had used a #import in C. It should be completely irrelevant to anyone who uses the class Fraction whether the < operator is implemented by using a trait or by writing a method in the class itself. Because of this, a trait cannot be used as a type -- you cannot, for instance, declare a variable of type TRelation. This is also the reason why you write use declarations in the body of the class and not the header, since it is an implementation detail and not something should know about to use the class.

A trait is only allowed to contain methods, not fields. This might seem like a considerable limitation but it simplifies the model considerably and if a trait needs a field it can simply require the class to have it. For instance, this trait which contains methods for dynamically extending an array or vector, needs a contents field:
trait TVector {

require accessor contents;
require accessor contents=(var value);

void ensure_capacity(int new_size) {
if (new_size > this.contents.size) {
// ... enlarge contents ...
}
}

}
TVector does not have the field contents itself but requires any class that uses it to have accessors for it. A class that uses TVector can implement this by actually having a field contents but since accessors are just ordinary methods (you can think of them as getter and setter methods), the class is free to implement them however it wants.

Our traits are very closely modeled on the trait mechanism for smalltalk described in the article linked above (and again here for your convenience) which I can definitely recommend reading. There are plenty of subtleties to the mechanism that I haven't mentioned. What happens if there's a name clash when importing more than one trait? (There's a mechanism for renaming methods) Can traits use other traits? (Yes) How does super calls work with trait methods? (I can't answer that in a single sentence). For the full answers to all this you'll just have to read the article. The original motivation for adding traits was the trouble we've had with implementing the collection hierarchy. It turns out that some of the people who wrote the smalltalk traits article have also written another one on exactly that: using traits in the design of a collection hierarchy.

So far we haven't used them that much in our libraries -- my guess would be that there is at most one trait to every ten classes. But in the cases where we have used them, they've really saved the day.

Saturday, April 08, 2006

Exceptions

It seems that these days whenever anyone describes some language feature, I'm compelled to compare/contrast that with how we've done things in the new neptune language. Most recently James Robertson has written about exceptions in smalltalk so, well, here we go.

In smalltalk exceptions capture the stack when they're thrown which is a bit similar to the way stack traces are captured by exceptions in java. In java, though, what is stored in the exception is exactly a stack trace which is a serialized copy of the real stack that you can print and... well, not much else. In smalltalk the exception actually captures the real live stack which means that execution can be resumed from wherever the exception was thrown. This borders on what MenTaLguY calls "the Lovecraftian madness of full continuations". But even though this model is neat and general, everyone I've ever talked with about resumable exceptions, at least those that have used them in practice, have told that their usefulness is actually pretty limited. James' example code does little to change my attitude; that code is so convoluted that I take it more as an argument against than for resumable exceptions.

My attitude against resumable exceptions may just be a convenient rationalization, though. On the OSVM platform, which must be able to run on platforms with very little memory and processing power, we can't afford the full smalltalk model anyway. In the previous incarnation of OSVM, which was based on smalltalk, there were no stacks or stack traces. You could throw any object as an exception and if someone else caught it there was no way to tell where it had been thrown from. In addition, there was no exception hierarchy and we usually just threw text strings or symbols: calling a method that hadn't been defined caused the system to simply throw the string "LookupError" with no further information about which method was called on which object. Clearly there was room for improvement and now that we've had an opportunity to revisit the design, we've improved it quite a bit. The biggest change we've made has to do with, you guessed it, the stack. But that aspect will have to wait a bit.

The old system allowed you to throw any object so there was really no technical reason not to have an exception hierarchy and throw structured and informative exceptions rather than flat text strings. We just never really got around to it. For the new language we were rewriting all our libraries anyway so we added structured exceptions as soon as we had implemented exception handling. And, I must confess, doing it early on also meant that it would be a major hassle to take them out later if it turned out that someone thought it was a bad idea. Did I mention that the code name of our next release is "sneaky monkey"? Anyway, now when an error occurs somewhere because you're trying to reverse a string (which you can't currently do) you'll an error that tells you exactly that: LookupError("ksiftseh".reverse()).

In neptune, the syntax for exception handlers is somewhat similar to C++, Java and C#. Here's how an exception handler looks in C++:
try {
...
} catch (exception& e) {
...
}
This exception handler catches any objects thrown that descend from the class exception, because that's the declared type of the exception variable e. Java and C# use the same approach. In neptune we have to use a slightly different model because we have optional typing, which means that type declarations are not allowed to affect the runtime semantics of a program. Here's an example of a neptune exception handler:
try {
...
} catch (Exception e: IOException) {
...
}
The exceptions handled by this catch clause are listed after the colon, in this case IOException, and the declared type of the variable, Exception, has no influence on which exceptions are caught. One thing this means is that you can now catch several unrelated exceptions with the same exception handler:
try {
...
} catch (Exception e: IOException, AssertionError) {
...
}
If the variable has the same type as the exception being caught, which is usually the case, you can just leave out the type declaration:
try {
...
} catch (e: IOException) {
...
}
The try/catch syntax is not "magic" but a shorthand that, like all our other shorthands, expands straightforwardly into simple method calls. A try statement corresponds to a call to the try_catch method on Function. The handler above expands into something like this
fun {
...
}.try_catch(new Vector {
IOException.protocol,
AssertionError.protocol
}, fun (Exception e) {
...
})
Hey I didn't say that the result was straightforward, just that the transformation was. And even though it may look expensive, with allocations and whatnot, the generated code is very efficient no matter if you use the try/catch syntax or write the call to try_catch yourself. An explanation of what .protocol means will have to wait.

Now for the stack traces. As you'll remember, in Java and smalltalk the stack is stored in the exception object being thrown. I'm not one to underestimate the ingenuity of the people that implement smalltalk or java (since a majority of my colleagues have done both) but no matter how clever you are, storing the stack in an exception is bound to cost space and time. The worst part is that when creating or throwing an exception in smalltalk or java, you don't actually know if the stack will be used or not so you always have to store it, just in case. That won't do in OSVM because we don't have processing power or space enough to do stuff "just in case". Instead, you can specify that you want a stack trace at the location where you'll be using it: the exception handler:
try {
...
} catch (e: IOException) at (StackTrace s) {
...
}
This way the stack trace and the exception object are completely decoupled and we only need to instantiate the trace when it is really needed. Another advantage of decoupling the trace is that throwing an object doesn't change it. In smalltalk, throwing an exception that has been thrown before causes the previous stack to be overwritten, which is not an especially clean model. In Java the stack trace stored in the exception is captured when the exception is instantiated, not when it is thrown. In my experience that is the desired semantics only in one situation: if you want to get the stack trace but don't want to throw an exception. Doing
new Throwable().printStackTrace()
is not uncommon in java but it's a hack really. In neptune there's a much cleaner way to get the current stack: just call the static Thread.current_stack() method.

I don't know of any other language with a similar model but my experience so far is that it is exactly what you want and, in addition, simpler to implement and less expensive than the alternatives. There are still issues to work out, like what happens when you rethrow an exception and what exactly should be stored in a stack trace object. The biggest problem for me, though, is the keyword at. This is something we can still easily change so if you have a good alternative let me know.