MoonScript’s class system is great balance of functionality and brevity. It’s simple to get started with, doesn’t impose many restrictions, and is incredibly flexible when you need to do advanced things or bend the rules.

Even if you have no intention of using MoonScript, understanding the class system implementation is a good exercise for understanding some of the more complicated parts of Lua.

A simple example

Lets start with a typical class in MoonScript:

class Player new: ( @x , @y ) => say_hello: => print "Greetings! I'm at #{@x}, #{@y}"

And take a look at the generated Lua: (Warning: there’s a lot going on, scroll past for analysis of each component)

local Player do local _base_0 = { say_hello = function ( self ) return print ( "Greetings! I'm at " .. tostring ( self . x ) .. ", " .. tostring ( self . y )) end } _base_0 . __index = _base_0 local _class_0 = setmetatable ({ __init = function ( self , x , y ) self . x , self . y = x , y end , __base = _base_0 , __name = "Player" }, { __index = _base_0 , __call = function ( cls , ...) local _self_0 = setmetatable ({}, _base_0 ) cls . __init ( _self_0 , ...) return _self_0 end }) _base_0 . __class = _class_0 Player = _class_0 end

Lets go from the outside in. The result of the class expression is a new local variable called Player . Nothing else is made available on the calling scope.

The class’s internal objects are created inside of a Lua do end block, this ensures that they are scoped to just the class in question. The two internal objects are _class_0 and _base_0 .

The resulting local, Player is assigned _class_0 .

The numbers at the end of these variables are not fixed, they come from MoonScript’s local name generator. They will increment if you nest classes. You should never write code that depends on their names.

The class object

The class object, aka _class_0 in the generated code, is a Lua table that represents the class. To create a new instance we call the class object as if it were a function. We can see here that it’s not actually a function.

In order to make a Lua table callable it must implement the __call metamethod.

Here’s the extracted class object’s creation:

local _class_0 = setmetatable ({ __init = function ( self , x , y ) self . x , self . y = x , y end , __base = _base_0 , __name = "Player" }, { __index = _base_0 , __call = function ( cls , ...) local _self_0 = setmetatable ({}, _base_0 ) cls . __init ( _self_0 , ...) return _self_0 end })

The Lua function setmetatable sets the metatable of the first argument to the second argument. It then returns the first argument. This means the value of _class_0 is the modified version of the first table.

The table _class_0 is very basic. It has the constructor we created (with new ) stored in __init , the base object stored in __base and the name of the class stored in __name .

Unlike the generated names, these names are unchanging and safe to use in your code. Because they are stored directly on the class object we can access them with dot syntax:

print ( Player . __name )

Two metafields are provided on the class objects metatable: __index and __call .

The __call function is what is called when we create a new instance: Player() It’s responsible for creating a new table to be the instance, providing it with a metatable, then calling the constructor.

You can can see how the _base_0 is used directly as the metatable of the object.

Additionally, the class object has an __index metafield set to the base. This has a lot of implications. The most important is you can access any fields from base directly on the class object, assuming they haven’t been shadowed by any fields directly on the class object.

The base object

local _base_0 = { say_hello = function ( self ) return print ( "Greetings! I'm at " .. tostring ( self . x ) .. ", " .. tostring ( self . y )) end } _base_0 . __index = _base_0 _base_0 . __class = _class_0

The base object, __base_0 is a regular Lua table. It holds all the instance methods of the class. Our example from above implemented a say_hello method which is compiled directly into the base.

The base object has a circular reference to itself in the __index field.

This lets us use the base object directly as the metatable of instances. The __index property is where instance methods are fetched from. Since it points to itself, the instance methods can be pulled directly from the metatable without any indirection.

Likewise, this also lets us implement other metamethods directly as instance methods of the class. I'll have an example below.

It’s a very cool concept, and definitely worth taking a moment to understand.

Lastly, a reference to the class placed on the base object with the name __class . This is how the @@ operator accesses the class object.

Classes with inheritance

Super invocation has changed a bit in MoonScript 0.4.0

Classes that inherit from other classes in MoonScript introduce a few more ideas. The extends keyword is used for inheritance:

class SizedPlayer extends Player new: ( @size , ... ) => super ... say_hello: => super ! print "I'm #{@size} tall"

Here’s the resulting Lua:

local SizedPlayer do local _parent_0 = Player local _base_0 = { say_hello = function ( self ) _parent_0 . say_hello ( self ) return print ( "I'm " .. tostring ( self . size ) .. " tall" ) end } _base_0 . __index = _base_0 setmetatable ( _base_0 , _parent_0 . __base ) local _class_0 = setmetatable ({ __init = function ( self , size , ...) self . size = size return _parent_0 . __init ( self , ...) end , __base = _base_0 , __name = "SizedPlayer" , __parent = _parent_0 }, { __index = function ( cls , name ) local val = rawget ( _base_0 , name ) if val == nil then return _parent_0 [ name ] else return val end end , __call = function ( cls , ...) local _self_0 = setmetatable ({}, _base_0 ) cls . __init ( _self_0 , ...) return _self_0 end }) _base_0 . __class = _class_0 if _parent_0 . __inherited then _parent_0 . __inherited ( _parent_0 , _class_0 ) end SizedPlayer = _class_0 end

The majority of the generated code is the same as a regular class. Here are the differences:

local _parent_0 = Player

There’s a new local variable inside the do end block called _parent_0 that holds a reference to the parent class.

local _base_0 = { } _base_0 . __index = _base_0 setmetatable ( _base_0 , _parent_0 . __base )

The metatable of the base is set to the base of the parent class. This establishes the inheritance chain for instances. If a method can’t be found on the class’s base, then the parent class’s base is automatically searched due to how __index works.

There’s a slight disadvantage to this. Metamethods are fetched with rawget , so metamethod inheritance does not work by default. We can work around this with the __inherited callback discussed below.

local _class_0 = setmetatable ({ __parent = _parent_0 }, { }

The parent class is stored on the class object in a field called __parent . This gives you an easy way to reference the parent class object.

{ __index = function ( cls , name ) local val = rawget ( _base_0 , name ) if val == nil then return _parent_0 [ name ] else return val end end , }

The __index metafield on the class object is now a function, instead of a reference to the base (which is a table). rawget is used control the precedence of the properties. If the field can’t be found directly on the base then the parent class is searched.

Remember that class objects also pull fields from their bases, so this has the effect of searching both the parent class object and the parent class’s base. Even though we've used rawget on the base, we can still get access to the parent class’s base.

if _parent_0 . __inherited then _parent_0 . __inherited ( _parent_0 , _class_0 ) end

Lastly, we now have a class callback. When a subclass is created and the parent class has a method __inherited then it is called with the class object that has just been created.

The __inherited method works directly with class objects, no instances are involved.

local _base_0 = { say_hello = function ( self ) _parent_0 . say_hello ( self ) return print ( "I'm " .. tostring ( self . size ) .. " tall" ) end }

In the example I included a method that calls super . All MoonScript does is provide sugar for calling the method of the same name on the parent class.

Class tips and tricks

Now that you have an understanding of how a class in MoonScript is implemented, it’s easy to see how we can work with the internals to accomplish new things.

Adding __tostring and other metamethods

If you want your instances to have a string representation you can implement a __tostring method in the metatable.

As we saw above, the metatable has an __index field set to itself, we just need to implement metamethods as instance methods:

class Player new: ( @x , @y ) => __tostring: => "Player(#{@x}, #{@y})" print Player ( 2 , 8 )

All of Lua’s metamethods work (except __index , see below). Here’s an example of a vector class with overloaded operators:

class Vector new: ( @x , @y ) => __tostring: => "Vector(#{@x}, #{@y})" __add: ( other ) => Vector @x + other . x , @y + other . y __sub: ( other ) => Vector @x - other . x , @y - other . y __mul: ( other ) => if type ( other ) == "number" Vector @x * other , @y * other else Vector @x * other . x + @y * other . y print Vector ( 1 , 2 ) * 5 + Vector ( 3 , 3 )

I mentioned above that metamethod inheritance does not work:

class Thing __tostring: => "Thing" class BetterThing extends Thing print BetterThing !

We can work around this by using the __inherited callback:

class Thing __tostring: => "Thing" __inherited: ( cls ) => cls . __base . __tostring = @__tostring class BetterThing extends Thing print BetterThing !

Adding a new method to a class after declaration

Now that we know about __base it’s easy to add new methods to classes that don’t have them.

class Player new: ( @name ) => Player . __base . jump = => print "#{@name} is jumping!" Player ( "Adam" ) \ jump !

We can extend this concept even further to dynamically generate methods:

class Player new: ( @name ) => for dir in * { "north" , "west" , "east" , "south" } @__base [ "go_#{dir}" ] : => print "#{@name} is going #{dir}" Player ( "Lee" ) \ go_east !

Converting an existing table to an instance

Sometimes you might already have a table that you'd like to convert to an instance of a class without having to copy it. Now that we know how the __init method works we can use setmetatable to accomplish a similar result:

class Rect area: => @w * @h some_obj = { w: 15 , h: 3 } setmetatable ( some_obj , Rect . __base ) print some_obj \ area !

This same method can be used to convert on object from type to another.

Adding __index metafield to an instance

MoonScript uses the __index metafield on class instances in order to allow instance properties to be looked up. If we just replace __inde with another implementation without any consideration we would break the instance. We'll have to chain our custom __index with the old one.

Here’s how we might implement getter methods:

class Thing getters: { age: => os.time ! - @created_at } new: => @created_at = os.time ! mt = getmetatable @ old_index = mt . __index mt . __index = ( name ) => if type ( old_index ) == "function" if gs = old_index @ , "getters" if gs [ name ] return gs [ name ] @ old_index @ , name else if g = old_index . getters and old_index . getters [ name ] g @ else old_index [ name ] t = Thing ! print t . age

Its’s important that you don’t try to access self (without rawget ) within the __index metamethod, otherwise you'll cause an infinite loop.

Writing that massive implementation in the constructor isn’t ideal. Here’s a base class that automatically upgrades anyone who inherits with getter functionality:

class HasGetters getters: {} __inherited: ( cls ) => old_init = cls . __init cls . __init = ( ... ) => old_init @ , ... mt = getmetatable @ old_index = mt . __index mt . __index = ( name ) => if getter = old_index . getters [ name ] getter @ else if type ( old_index ) == "function" old_index @ , name else old_index [ name ] class BetterThing extends HasGetters getters: { age: => os.time ! - @created_at } new: => @created_at = os.time ! t = BetterThing ! print t . age

The clever part here is replacing the __init method on the base class with a custom one that automatically injects support for getters.

Future improvements

The class system is far from perfect. Here are some future improvements that I'd like to add:

There’s no way to determine which order methods are added to a class. If you're going to be triggering side effects from method creation then your options are limited.

The MoonScript class meta-properties use double underscore just like Lua. If Lua ever decides to use any of the same names then there will be conflicts.

Closing

Not all of the functionality of MoonScript classes was covered in this guide. You can learn more on the Object Oriented Programming section of the MoonScript documentation.