An introduction to MoonScript

Lua is an underrated programming language. If the task doesn’t require many libraries, it’s my go-to-choice. This is somewhat of an oddity as even most Lua programmers seem to prefer C. But the more you use any tool, the more you realize it has some faults. Lua is, sadly, no exception. First and foremost, there is a massive shortage of libraries and tooling. Lua does not come ‘batteries-included’ like Python or Ruby. It does not come with a package manager. There isn’t even a standard linter (luac -p is closest). This is because Lua is an embeddable language, first and foremost. It forgoes the batteries. It expects you, the embedder, to provide them. LuaRocks is the most popular package manager for Lua and is easiest method to provide batteries.

Secondly, Lua is a minimal language. It lacks integers, ++/+= operators, bit operators, classes, continue, switch, the ternary operator, regular expressions and a few other features I rarely miss. It is quirky in other areas. Lua has only one data structure: the table (an associative array that’s also optimized for normal array use). It has no class system. But the quirks are workable and, in some cases, advantageous. The most unexpected failure of Lua, however, is perhaps an oversight: variables are global by default. Misspell a variable used in an assignment? Congratulations, you’ve created a new global variable! Lua does, however, possess the features I want in a modern language: lexical scoping, coroutines, first class functions, closures, tail recursion, an incremental, generational garbage collector and a wickedly fast JIT compiler with an easy to use foreign function interface.

MoonScript is a practically unknown but highly useful language which aims to improve on Lua. MoonScript is to Lua what CoffeeScript is to JavaScript. It compiles a syntax similar to CoffeeScript into valid Lua. MoonScript does solves many of the ‘problems’ of Lua. It adds continue, switch, +=, function shorthand, a default class system, table comprehensions, slices, destructured assignments, default parameters, and a diabetes-inducing amounts of sugar. Most importantly, variables are local by default. MoonScript is extremely terse, typically reducing the character count by 20-30%. So let’s take a brief look at MoonScript (and a bit of Lua).

Ubuntu/Debian

sudo apt-get install lua5.1 luarocks
sudo luarocks install moonscript

Or you can install LuaRocks from GitHub.

sudo apt-get install lua5.1
git clone git://github.com/keplerproject/luarocks.git
cd luarocks
./configure
make
sudo make install
sudo luarocks install moonscript

Windows

Download and install Lua for Windows. This installs Lua 5.1, LuaRocks, and a whole slew of libraries. Then download the MoonScript binary and extract it to somewhere on your path.

Getting Started

Now that we have lua (the interpreter), moonc (the MoonScript compiler), and luarocks (the package manager) installed we can add the batteries. There are a number of libraries that are essential for most uses. Now we can create an interesting first program. We’ll create a derivative of the permutation example from Programming in Lua (a good read if you’re interested in Lua).

There are two ways to run this program. During development you’ll want to use moon start.moon. moon rewrites errors such that the correct line number the error occured on is displayed. For deploying libraries you’ll want to use moonc start.moon. This builds a correspondingly named Lua file. moonc attempts to produce legible Lua code, but it uses mangled names. As an example let’s look at the Lua output for moonc start.moon.

and the output:

We have 24 permutations:
2,4,6,8
...
8,6,4,2

Yikes. That’s not very legible at first glance. The variable names are hideous but decipherable. _accum_0 is the accumulator table. _len_0 is the current index of that table. It should be clear how MoonScript constructs table comprehensions (it’s a glorified for loop). But you need to be cautious when using them. MoonScript will generate a table comprehension as an immediately executed function when passed directly to a function (i.e. without being assigned to a variable). This is functionally the same but it can reduce performance. Slices, akin to Python’s slices, can be used with comprehensions. Play with the code: switch from odds to evens and add slices.

Classes

Lua doesn’t have classes, but it does have objects (tables) and a metatable mechanism which simplifies the creation of a class system. Within the Lua community, the library middleclass is fairly common. Use any Lua class library with MoonScript you so desire. Or refuse to use a class library. Lua is kind enough to give you the option. But before you make any decision let’s look at an example of MoonScript’s class system.

and the output:

Sushi is currently sleeping.
Sushi says meow.
Sushi is currently scared.
Oswald is currently barking.
Oswald says roof roof roof roof roof.
Unknown animal is currently not on meep.
Cat: Sushi
Dog: Oswald
Animal: Unknown animal

I won’t bother showing the Lua code. It’s a mess. I find the class system to be conceptually attractive but it is not ideal for my uses. First, it doesn’t inherit metamethods by default. This is a safe choice, but it is the reason for the Object class in the above example. Secondly, the code it produces would draw ire when committed to a pure Lua repository. If you’re working on a Lua project, don’t bother with MoonScript’s class system. Use middleclass or the closure-based approach. I find the closure-based approach to work best in practice. It avoids MoonScript’s irregular \ method invocation operator and allows for true private variables (as upvalues).

Conclusion

I would cover a few other important features, but the very clear manual renders that a waste. It turns, then, to thoughts. MoonScript offers the sugar that Lua lacks. It’s great for writing short scripts or tools. Using MoonScript, I see myself breaking scripts into multiple, smaller files. This allows easy verification as the Lua fits within one or two pages. The average function size decreases and I see myself favoring short one or two line functions. MoonScript’s concise syntax allows for maximum brevity, without often compromising clarity.

Unfortunately, much like Lua, it has a number of problems and issues to overcome. MoonScript has an almost non-existent community. The error messages from the compiler are the uniformly uninformative: ‘Failed to parse’. Whitespace is significant, much like Python and CoffeeScript. If you’re not accustomed to significant whitespace, prepare for surprises. If you’re writing a large program in MoonScript, be sure to verify the Lua output from time to time. Compiling can be a chore, but luckily moonc -w $PWD watches your directory for changes and compiles immediately.

In the end, I can’t help but like MoonScript. In spite of its faults, it manages to increase productivity and decrease eyesore. I’ve added it to my toolbox, alongside Lua. If you find MoonScript interesting and would like to build something cool, check out Lapis (a web framework) and/or LÖVE (a Lua game engine).