MoonScript 0.8.0 - How to Call Methods

On This Page

All Pages

In MoonScript you call a method with a backslash: obj\method args compiles to obj:method(args) in Lua. The dot, obj.method args, looks up a field and calls it without passing the object as self. Inside a method, @ refers to self, so @method args calls another method on the same object.

Quick reference

MoonScriptLuaMeaning
obj\method a, bobj:method(a, b)Call a method, passing obj as self
obj\method!obj:method()Call a method with no arguments
obj.func aobj.func(a)Call a function stored in a field, no self
@nameself.nameRead a field on self
@method aself:method(a)Call a method on self
f = obj\method(a closure)Create a function bound to obj

Calling a method with \

Lua uses : to call a method. MoonScript uses \ instead. Arguments can be written without parentheses, and ! calls a method with no arguments:

MoonScript
file = io.open "notes.txt"
contents = file\read "*a"
file\close!
Lua
local file = io.open("notes.txt")
local contents = file:read("*a")
file:close()

Chained calls work as you would expect, as do calls on the result of an index or on a literal:

MoonScript
name = user.profile\display_name!
upper = "hello world"\upper!
result = builder\select("id")\where(active: true)\limit 10
Lua
local name = user.profile:display_name()
local upper = ("hello world"):upper()
local result = builder:select("id"):where({
  active = true
}):limit(10)

All of Lua’s string methods work this way, since strings use the string table as their metatable:

MoonScript
str = "MoonScript"
print str\sub 1, 4
print str\lower!, str\len!
Lua
local str = "MoonScript"
print(str:sub(1, 4))
print(str:lower(), str:len())

Why Lua’s : doesn’t work

The colon is already used in MoonScript to write key and value pairs in tables. Writing a Lua-style method call does not cause a compile error. It creates a table instead:

MoonScript
contents = file:read "*a"
Lua
local contents = {
  file = read("*a")
}

The code compiles, and fails when it runs. The compiled Lua calls a global function named read, which doesn’t exist:

lua5.4: notes.lua:1: attempt to call a nil value (global 'read')

On Lua 5.1 and LuaJIT the same error is worded differently:

luajit: notes.lua:1: attempt to call global 'read' (a nil value)

If you see attempt to call a nil value (global '...') for something that looks like a method name, look for a : that should be a \.

The difference between \ and .

The dot accesses a field and calls whatever is there as an ordinary function. No self is passed. This is correct for module functions and for functions stored in a table:

MoonScript
json = require "cjson"
encoded = json.encode { 1, 2, 3 }
Lua
local json = require("cjson")
local encoded = json.encode({
  1,
  2,
  3
})

Using a dot to call a method is a mistake. The method receives no self, and fails as soon as it reads a field:

MoonScript
class Person
  new: (@name) =>
  greet: => print "Hello, I am #{@name}"

person = Person "Ada"
person.greet!
Lua
local Person
do
  local _class_0
  local _base_0 = {
    greet = function(self)
      return print("Hello, I am " .. tostring(self.name))
    end
  }
  _base_0.__index = _base_0
  _class_0 = setmetatable({
    __init = function(self, name)
      self.name = name
    end,
    __base = _base_0,
    __name = "Person"
  }, {
    __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
  Person = _class_0
end
local person = Person("Ada")
person.greet()

Running this gives:

lua5.4: person.lua:6: attempt to index a nil value (local 'self')

On Lua 5.1 and LuaJIT:

luajit: person.lua:6: attempt to index local 'self' (a nil value)

The fix is person\greet!.

Using @ inside methods

A method is a function defined with the fat arrow, =>. The fat arrow adds a self argument, and inside the method @ is shorthand for self:

MoonScript
class Counter
  new: =>
    @count = 0

  increment: (amount=1) =>
    @count += amount
    @report!

  report: =>
    print "count is #{@count}"

@count reads a field, and @report! calls a method, compiling to self:report(). When @ is followed by a name and then arguments, it is a method call:

MoonScript
class Greeter
  greet: (name) => print "Hello, #{name}"
  greet_all: (names) =>
    for name in *names
      @greet name

To call a function stored on self without passing self, index it with a dot: @.callback value compiles to self.callback(value).

=> vs ->

A function defined with the thin arrow, ->, gets no self argument. Using -> for a method, and then calling it with \, shifts every argument by one position, because the object fills the first parameter:

MoonScript
class Tag
  set_name: (name) -> @name = name
Lua
local Tag
do
  local _class_0
  local _base_0 = {
    set_name = function(name)
      self.name = name
    end
  }
  _base_0.__index = _base_0
  _class_0 = setmetatable({
    __init = function() end,
    __base = _base_0,
    __name = "Tag"
  }, {
    __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
  Tag = _class_0
end

Here @name refers to a self that the function never defines. Use => for anything that will be called as a method, including new.

Getting a method as a function

Sometimes you need to pass a method somewhere that expects a plain function, such as a callback. Writing obj\method without calling it creates a new function with obj bound as self:

MoonScript
on_click = button\handle_click
register "click", on_click
Lua
local on_click
do
  local _base_0 = button
  local _fn_0 = _base_0.handle_click
  on_click = function(...)
    return _fn_0(_base_0, ...)
  end
end
register("click", on_click)

The same works with import. Prefixing a name with \ imports it as a function bound to the object:

MoonScript
import \open, \close from session
open!
Lua
local open, close
do
  local _obj_0 = session
  open, close = (function()
    local _base_0 = _obj_0
    local _fn_0 = _base_0.open
    return function(...)
      return _fn_0(_base_0, ...)
    end
  end)(), (function()
    local _base_0 = _obj_0
    local _fn_0 = _base_0.close
    return function(...)
      return _fn_0(_base_0, ...)
    end
  end)()
end
open()

Calling MoonScript methods from Lua

MoonScript classes compile to ordinary Lua tables and metatables, so Lua code calls their methods with the normal : syntax:

local Person = require("person")
local p = Person("Ada")
p:greet()

See also