The JVM is an interesting place these days. We'll have Java 7 soon, with support for dynamic invocation and improved APIs. We have a many programming languages to choose from — some statically typed, some dynamically typed, some blurring the lines — that offer their own unique benefits. We're even getting a few "small changes" to Java itself, like literal lists and Strings-in-switch. We have the largest and most robust ecosystem of any managed runtime, with hundreds of conferences, tens of thousands of libraries, and millions of developers.

But there's something missing. No language seems likely to replace Java itself.

Or at least, none of the alternatives fits what I need out of a Java replacement.

Learning from Java

Let's take a look at the criteria I believe we must meet to replace Java. These are my opinions, but they're based on many of the guiding principals that have made Java such a success.

Performance equivalent to Java for equivalent code

If we're going to start writing core libraries and systems-level code in this new language, it must perform exactly as well as Java does. We can certainly attempt to perform better, but parity is an absolute minimum.

No language-imposed runtime library

Perhaps the biggest barrier to using current alternatives is that they impose upon you their "ball and chain" runtime library. You can't write "Hello, world" without shackling yourself to a 5, 10, maybe even 20MB jar file. That complicates deployment and makes small-profile applications more difficult. Even worse, some languages carry a large initialization cost to load their runtime's class files and/or initialize runtime state.

No more (or not much more) complicated than Java

Java's success lies heavily in its simplicity. Any language intended to replace it needs to a suitable "first language." That doesn't mean we can't make it more expressive or more powerful than Java, but we must be careful how far we take it.

Beautiful

For all its positive traits, Java is not a particularly beautiful language. There's a tremendous amount of "ceremony" required for even the simplest programs. Types have to be explicitly declared everywhere (Java 7's "diamond operator" notwithstanding). It's impossible to write "Hello, world" using less than 80 characters, when most other languages can do it in under 20.

Perfect integration with JVM libraries

Any language that replaces Java needs to fit Java's use cases. That means being able to define real classes, real static methods, real arrays. Calls from our language to Java need to be direct invocations, using the same bytecodes and imposing no more overhead than the same calls written in Java. Where we use Java now, we need to be able to use our language. If we can't — we'll always need Java somewhere in the stack.

Extensible

Languages now compete to see who makes it easiest to design DSLs, who has the easiest extension points. Extending Java is essentially limited to what you can do with annotations and annotation processors, and forget about having a flexible syntax — Java looks like Java. Our hypothetical language needs to be easy to extend and it needs to make it easy to experiment with new features.

Is it possible to meet all these requirements? I believe it is, and my attempt to do so is called Mirah.

The Mirah Programming Language

Mirah grew out of my desire to have a language for implementing JRuby that would be more approachable to non-Java developers. Let's face it, Java's not too hard to learn, but there are lots of details that take time to internalize. It's not a complicated language, but it can scare the uninitiated. With Mirah, I wanted to make a language that fit my criteria, so that I and others would have the Java replacement we've been wanting.

It's easier to explain Mirah with code. Let's have a look.

Installing Mirah

The easiest way to get Mirah is to download the most recent .zip file from Mirah's Github Downloads page.

Inside you'll find mirah-complete.jar, some bin scripts for "mirah" and "mirahc," a set of examples (some of which you will see in this article), and the usual README and LICENSE flotsam.

Alternatively, if you are a JRuby user, you can simply "gem install mirah" and you're ready to go.

Getting Started

Mirah has a clean, simple syntax. Some might call it beautiful, and many of you will recognize it as being very similar to Ruby. That's no accident; when I kept hearing other JVM languages compare themselves to Ruby, I realized I could simply steal Ruby's syntax. Sneaky, eh?

puts "Hello, world!"

We have our first program in Mirah. Let's run it with the mirah command:

~/projects/mirah_play → mirah -e 'puts "Hello, world!"' Hello, world!

Here we're using the -e flag to execute an inline script. We can also put this into a file:

~/projects/mirah_play → echo 'puts "Hello, world!"' > hello.mirah ~/projects/mirah_play → mirah hello.mirah Hello, world!

Of course I mentioned that Mirah is also a compiler. In the above examples it compiles the given script immediately before running it; no runtime library means no interpreter, so everything ends up as JVM bytecode before execution. We use the mirahc command to produce .class files ahead of time:

~/projects/mirah_play → mirahc hello.mirah ~/projects/mirah_play → ls -l Hello.class -rw-r--h;r-- 1 headius staff 505 Mar 19 18:49 Hello.class ~/projects/mirah_play → java Hello Hello, world!

This example may be the most important. Not only does Mirah produce a tiny classfile for this particular piece of code, but that classfile is completely standalone; the subsequent java command needs no jar files or CLASSPATH. You get to decide what dependencies to introduce into your program. As it should be.

Let's review some basic Mirah syntax.

Basics

Mirah is statically typed like Java or Scala, but you might not know just from looking. That's because Mirah employs a technique called "local type inference." In simple terms, Mirah can usually infer the types of objects, variables, and method calls from their context. Like Scala and C#, you must still declare method parameters; Mirah only infers types in a local context. Let's take a simple method definition in Java and compare it to idiomatic Mirah. First, the Java:

public static String hello(String name) { return "Hello, " + name + "!"; }

And now the Mirah version:

def hello(name:String) "Hello, #{name}!" end

Here we have declared the type of the name variable as String. Because the only line of code here is a String, we know the return type must be String. The string itself is "interpolated" to clean up the + concatenation from the Java example. The semicolon is gone, because in Mirah line-terminating semicolons are optional. The return is gone, since Mirah will simply return the last expression in the method (but you can still use return to exit a method early). And public static and the braces have been replaced with def and end.

If we put this into a file, we get pretty much the same result as using javac:

~/projects/mirah_play → cat HelloJava.java public class HelloJava { public static void main(String[] args) { System.out.println(hello("world")); } public static String hello(String name) { return "Hello, " + name + "!"; } } ~/projects/mirah_play → cat hello_mirah.mirah def hello(name:String) "Hello, #{name}!" end puts hello("world") ~/projects/mirah_play → javac HelloJava.java ~/projects/mirah_play → mirahc hello_mirah.mirah ~/projects/mirah_play → javap HelloJava Compiled from "HelloJava.java" public class HelloJava extends java.lang.Object{ public HelloJava(); public static void main(java.lang.String[]); public static java.lang.String hello(java.lang.String); } ~/projects/mirah_play → javap HelloMirah Compiled from "hello_mirah.mirah" public class HelloMirah extends java.lang.Object{ public static void main(java.lang.String[]); public static java.lang.String hello(java.lang.String); public HelloMirah(); }

Mirah treats the top-level of a script as though it's the "main" body. Methods defined there are turned into static methods on the same class. This allows you to have very lightweight scripts that are very clean, but without any runtime dependency.