Minnow

Minnow is a toy language, currently under development, intended to be used as a basis for experiments in language enhancements. The concept was to have a minimal OO language (min-OO -> Minnow) to use as a base that’s easy to extend. A language that’s as simple as possible, while still being strongly-typed and object-oriented.

Simple here doesn’t necessarily mean easy to use, terse, flexible, or forgiving. Quite the opposite. Simple means simple for me, the language designer, not you, the language user (aka: future me).

Plus, it doesn’t do anything. Yet. So far, all I have is a parser and a poorly-defined specification.

That said, here are some of the main design principles of Minnow.

1. The static type system reigns supreme.

There is no such thing in Minnow as a run-time type. There is no null, no casting, and no reflection. Classes aren’t types: only interfaces are, and each class has to specify what interface it fulfils. Each class can only implement one interface (although interfaces can extend multiple interfaces).

Types have one responsibility: to ensure that the methods you call on objects exist, with the correct name, and number and type of arguments. As such, Minnow has structural subtyping of interfaces:

interface CanWalk {
   method walk[] returns Unit;
}

interface CanWalkAndChewBubblegum {
   method walk[] returns Unit;
   method chewBubblegum[] returns Unit;
}

Here, CanWalkAndChewBubblegum is a subtype of CanWalk, because anything a CanWalk can do (ie, walk), a CanWalkAndChewBubblegum can do too, even without specifying any sort of relationship between the two types.

interface CanChewBubblegumAndWalk extends CanWalk {
   method chewBubblegum[] returns Unit;
}

CanChewBubblegumAndWalk is an equivalent type to CanWalkAndChewBubblegum: an instance of either can be assigned to the other.

This gets slightly more complicated when considering covariance and contravariance, and quite a lot more complicated when considering generic types. But we’ll get to that later.

2. Classes are kind of gimped.

I’ve already mentioned classes aren’t types, and everything which refers to objects does so via interfaces. I’m also removing the following from the feature-set of classes:

  • Inheritance: Classes stand alone. As classes aren’t types, the subtyping relationship is achieved solely through interface implementation. Code reuse, the other function of implementation inheritance, can be achieved just as simply via composition – not as tersely, but far more explicitly. Syntactic sugar to aid composition will probably be a very early extension here.
  • Overriding and overloading: Without inheritance, overriding is a meaningless concept. Meanwhile, overloading is a lie – it’s rife with horrible corner-cases and unintuitive behaviours, and leads to all sorts of subtle programming errors and maintenance issues. Also, it’s hard to implement, especially with structural subtyping.
  • Multiple constructors: This is a special case of the removal of overloading, and (in my experience) a frequent gathering-point for maintenance issues when class invariants change. If you need different ways to build classes, that’s what factories are for. In fact, I’m tempted to introduce syntactic sugar for factories and bar public constructors completely, but that’s a bit down the road.
  • Access modifiers: If a method is part of the class’s interface, it’s public. If not, it’s private (proper private, so only it can see its own internals, none of this wishy-washy-other-objects-of-the-same-class crap). No need for protected, as we don’t have inheritance. We don’t need no stinking access modifiers.

3. Syntax and symbology.

Programming languages are filled with all sorts of obtuse and arbitrary symbology. If I have to construct a syntax anyway, I might as well do something about that – here, I tried to follow three simple rules:

  • Don’t have the same symbol mean multiple things.
  • Don’t use symbols where words are clearer.
  • Don’t use the symbols I’m used to just because I’m used to them.

The canonical example, to my mind, of bad symbology is:

 if ( a < b or a = b or a > b )

In C/Java/etc, at least: One of these things is not like the others. One of these things just doesn’t belong.

Various use-cases are best addressed with symbols, for issues of, well, universal familiarity. a + (b * 4) is the best way of expressing numerical operations, for example, and would be understood by those with a basic maths education and no exposure to programming. Similarly, I’m leaving the basic six comparison operators (<, <=, =, >, >=, !=) as is, with only != needing any explanation. As for symbols like %, ||, and so on… well, I’m unsold on exactly how key the mod operator is, and using “and” and “or” instead of “&&” and “||” just seems like an easy win to me.

So what to do about assignment, if I’m giving comparisons exclusive access to the = symbol? Something like this:

String a is "Hello!";
a becomes "Goodbye!";

To emphasise, there are separate syntaxes for initialisation and reassignment. Side note: that excerpt is illegal in Minnow, as references can’t be reassigned to unless they’re specifically declared as being variable.

What other symbols does Minnow use? Parentheses are used as above for precedence, and only for precedence. For defining lists of arguments/parameters for functions, instead it uses square brackets:

function eat[Food dinner] returns Poop;
eat[pizza];

What about array/list element access? Use list.get[] and list.set[], there’s no need for an operation that narrow to have its own special syntax (in C maybe it’s not so narrow, but here it is), and there’s only so many types of brackets to go round.

Why do they have to be brackets at all? Well, it’s important to be able to represent meaningfully the start and end of nested blocks: some things are easy to represent with separate start/end symbols, and difficult without. On a standard keyboard, there are only four obvious sets of open/close charaters: ( ), [ ], { } and < >. Of those, < > are out of the picture because those are being used for comparisons, and we’re avoiding reusing the same symbols in different contexts.

So with parentheses being used for precedence, and square brackets for argument lists, then that leaves us curly braces and one other obvious context that needs braces: definition blocks, just like C/Java. Also just like C/Java, statements end with a semi-colon. Which is a bit of a rubbish symbol to use as an end terminator, but I don’t want to use the obvious one because it’s got a big problem.

My main rule so far is that I can’t reuse the same symbol in different contexts. The best symbol to end a statement with is the full stop. That’s how we end sentences. But there are two other compelling uses for the full stop:

diner.eat[3.1415926];

If we want floating-point number literals, then there’s really no choice: the full stop is the separator. At least, in the UK/US – in Europe, it’s the comma. That’s way worse in terms of ambiguity, though:

diner.eat[3,1415926];
diner.eat[starter, main, dessert];

What about method invocation, though: does that have to be the full stop? There are some strong readability reasons why it’s an incredibly compelling symbol: it’s semi-whitespace, making it easy to separate the identifiers while still keeping them visibly bound. On this issue, I’m currently thinking the readability benefits outweigh the principle, and thus I’m breaking my own rule here.

So, on the semi-colon issue – I’m keeping it. For now. Maybe I can get rid of an end-of-statement symbol altogether (it certainly makes parsing easier), and maybe there’s a better choice. Right now it’s the default option.

In terms of defining return types, I like Scala’s approach:

def func(arg: Integer): Double = { return doSomethingTo(arg); }

But I don’t like those colons, and I don’t like that def. We can be more communicative than that:

function returnFive[] returns Integer { return 5; }

interface FiveReturner {
   method returnFive[] returns Integer;
}

class FiveReturner implements FiveReturner {
   construct[] { }
   method returnsFive[] returns Integer { return 5; }
}

Functions are called functions, methods are called methods, and constructors aren’t methods having a particular signature: they’re not methods at all, they’re constructors.

More coming soon.

Postscript: I’ve just discovered there’s already a Minnow programming language. Now I need a new name for it. Poopcakes.

Advertisements

One thought on “Minnow

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s