Lua output for moonscript classes is MASSIVE

1.8k views Asked by At

I'd like to be able to make a program for ComputerCraft using MoonScript, but due to the way that CC is sandboxed to prevent security issues on Minecraft servers and such, I can't require moonscript directly and run moonscript code from there. I have to convert my moonscript code to lua.

This is problematic, however, due to the fact that the class implementation for moonscript is very big, and not very filesize-conservative. When I type "class Bacon", it outputs this for lua:

local Bacon
do
  local _parent_0 = nil
  local _base_0 = { }
  _base_0.__index = _base_0
  if _parent_0 then
    setmetatable(_base_0, _parent_0.__base)
  end
  local _class_0 = setmetatable({
    __init = function(self, ...)
      if _parent_0 then
        return _parent_0.__init(self, ...)
      end
    end,
    __base = _base_0,
    __name = "Bacon",
    __parent = _parent_0
  }, {
    __index = function(cls, name)
      local val = rawget(_base_0, name)
      if val == nil and _parent_0 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 and _parent_0.__inherited then
    _parent_0.__inherited(_parent_0, _class_0)
  end
  Bacon = _class_0
  return _class_0
end

And this is for every class implementation, which is kind of ridiculous. Is there any way I can shorten this in my moonscript code?

3

There are 3 answers

0
leafo On

The amount of generated code has been cleaned up quite a bit in the latest version, 0.2.4: http://leafo.net/posts/moonscript_v024.html#code_generation_changes

A class now minimally looks like this

local Hello
do
  local _base_0 = { }
  _base_0.__index = _base_0
  local _class_0 = setmetatable({
    __init = function() end,
    __base = _base_0,
    __name = "Hello"
  }, {
    __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
  Hello = _class_0
end
0
daurnimator On

Just looking at the code I can remove some dead paths due to _parent_0 being nil...

local Bacon
do
  local _base_0 = { }
  _base_0.__index = _base_0
  local _class_0 = setmetatable({
    __init = function(self, ...)
    end,
    __base = _base_0,
    __name = "Bacon",
  }, {
    __index = function(cls, name)
       return rawget(_base_0, name)
    end,
    __call = function(cls, ...)
      local _self_0 = setmetatable({}, _base_0)
      cls.__init(_self_0, ...)
      return _self_0
    end
  })
  _base_0.__class = _class_0
  Bacon = _class_0
  return _class_0
end

You can find a static analyser to do this for you.

Otherwise if it's purely code size (in bytes) that concerns you, then use a compressor (e.g. Squish)

0
Oliver On

There is no way that you can shorten this in your moonscript code. But you might be able to post-process the output manually rather trivially to refactor common portions. Is it worth doing? Consider this:

  • If you want to use the notion of "class" as it is available in moonscript, then you have to code all those steps anyways (otherwise you get a "class" with different "class" behavior).
  • The difference is that in moonscript, N classes cause N copies of the code you posted, differing only by two lines each, whereas if you implemented moonscript's notion of class manually, you would probably use a function to define classes, as is done in some Lua OO libraries: Bacon = newClass("Bacon") would call the above code, with the "__name =" and "Bacon =" lines replaced appropriately.
  • So by copying moonscript code instead of code it manually, each class requires 20 lines of code (latest moonscript). But unless your classes are trivial, it is likely that they will contain a fair bit more code than 20 lines, probably 200.
  • So while it is true that you are duplicating 18 lines per class, this is likely less than 10% of the code you will have to write.
  • But that is an assumption. If the assumption is wrong, then the question is, why do you need classes in the first places? Just use basic Lua objects based on tables with ':' sugar.

If you really want use the moonscript output, I think your only option is to refactor it manually. The code you posted can be put in a function and only two lines need to be changed to parametrize for class name.

Note that there several advantages to leaving the Lua code duplication generated by moonscript in your Lua files, at least for oft-used operations (class definition is probably not one of them, but moonscript does way more than that):

  • it cuts back on function calls; this is likely to increase speed since no need to push variables onto a stack, call a function, put return values onto the stack, read stack, pop values off stack). Whether the speed increase is noticeable will depend a lot on how often the code is called.
  • it is more maintainable: if done right, you can likely update the code when moonscript gets updated more easily.