Policies for writing Lua modules

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Policies for writing Lua modules

Post by Roland_Yonaba »

Hi all,

I am hereby sharing with you all some short and nice articles for writing modules in Lua in a clean way.
Actually, the authors are not trying to define "the way to go" or stand as an authority, but they are just sharing their thoughts on the topic,
and their own policies they tend to stick to, mentionning eventually the pros (and cons).

I found the content very much valuable, as there are very much interesting things to learn from, and it might influence your actual style.
Plus, they are all written by some fairly experienced programmers, that's the very reason I am posting them here.

The article who originated is from Hisham Muhammad:
Link: How to write Lua modules in a post module world

The second article is a straight response to the first one. It is from Pierre Chapuis:
Link: How I write Lua modules

Prior to these article, there is also a great post from Kikito, which provides some nice policies/guidelines for module
privacy. The article raises some good points that are also mentionned in the previous articles. Also, it is based on
MiddleClass.
Link: Module privacy


As a complementary reading on the topic: Happy reading, share thoughts. :)
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Policies for writing Lua modules

Post by Inny »

I've taken to writing my code like this:

Code: Select all

local actor = {
  _VERSION = 'actor.lua 0.1',
  _COMPONENT = "actor",

  init = function(self)
    self._co = { dt = 0 }
  end,
}

actor.__index = {

  cowrap = function(self, func)
    self._co.thread = coroutine.create(function() func(self) end)
    return self
  end,

  corun = function(self, ...)
    if not self:codone() then
      local err, msg = coroutine.resume(self._co.thread, ...)
      if err == false then
        error(debug.traceback(self._co.thread, msg))
      end
    end
  end,

  cowait = function(self, secs)
    repeat
      local dt = coroutine.yield(true)
      if not secs then
        self._co.dt = 0
        return dt
      end
      self._co.dt = self._co.dt + dt
    until self._co.dt >= secs
    self._co.dt = self._co.dt - secs
    return secs
  end,

  codone = function(self)
    if not self._co.thread then return true end
    return (coroutine.status(self._co.thread)=="dead")
  end
}

return actor
This particular piece is part of an entity-component style I've adopted. I'm kind of living in a post-inheritance world where I like to do everything via copying, and I decided that the __index subtable is the perfect place to put the mixin functions.

But yeah, the return local module style has become the dominant form for Lua 5.2, and it's feeding back into 5.1 code, and even in the javascript world their way of declaring modules has been to just fill up and object with useful things.
User avatar
richardperkins
Prole
Posts: 9
Joined: Tue Jan 07, 2014 6:20 am

Re: Policies for writing Lua modules

Post by richardperkins »

These articles were extremely helpful, and I thank you for that.

I have adopted two styles from this based on whether I want the "module" to be instantiated or not.

Inny, your technique intrigues me. How exactly do you implement your modules and/or what is the purpose or perk(s) to modelling in that way?
bekey
Party member
Posts: 255
Joined: Tue Sep 03, 2013 6:27 pm

[]

Post by bekey »

-snip-
Last edited by bekey on Fri Jan 24, 2014 2:22 am, edited 1 time in total.
love2d.org forum removal spell: http://pastebin.com/iVqJrbKN
User avatar
qaisjp
Party member
Posts: 490
Joined: Tue Sep 04, 2012 10:49 am
Location: United Kingdom
Contact:

Re: Policies for writing Lua modules

Post by qaisjp »

Very interesting...
Lua is not an acronym.
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Policies for writing Lua modules

Post by Roland_Yonaba »

bekey wrote:Commenting as opposed to bookmarking.
Is this thread from the inner party? Or did I just not notice it first time around.
Nope, I posted it in the General section. Glad you find it useful :)
User avatar
SiENcE
Party member
Posts: 806
Joined: Thu Jul 24, 2008 2:25 pm
Location: Berlin/Germany
Contact:

Re: Policies for writing Lua modules

Post by SiENcE »

I have one general problem by writing modules/libs.

Do i include middleclass into the library...should i use my own class code (no good) or should i include a reference and the user has to get this library himself (possible version conflict).
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Policies for writing Lua modules

Post by Roland_Yonaba »

SiENcE wrote:I have one general problem by writing modules/libs.

Do i include middleclass into the library...should i use my own class code (no good) or should i include a reference and the user has to get this library himself (possible version conflict).
I think it all depends on you. Matter of taste.
Class implementations are useful, and I would personally use one in a large project, where several classes are created, where instantiation/inheritance is heavily used because it has to, to dispose of reusable code. Games, for instance.
When writing a module library (or a library consisting of several modules), I won't include a class library, because I do not feel like I should.
Rather, i will hardcode my classes, because it is very simple to do so. The lesser dependencies, the better.

Code: Select all

local ModuleClass = {}
ModuleClass.__index = ModuleClass
function ModuleClass:new(...)
  local instance = {...}
  -- some dirty work
  return setmetatable(instance, ModuleClass)
end
And if I want inheritance:

Code: Select all

local ModuleClass = {}
function ModuleClass:new(...)
  local instance = {...}
  -- some dirty work
  self.__index = self
  return setmetatable(instance, self)
end
Or maybe, if I need a generic class constructor, to be reused, I'll have this function internally defined:

Code: Select all

local class = function(...)
  local klass = {}
  klass.__index = klass
  klass.__call = function(_,...) return klass:new(...) end
  function klass:new(...)
    local instance = setmetatable({}, klass)
    klass.__init(instance, ...)
    return instance
  end
  return setmetatable(klass,{__call = klass.__call})
end
Also, your module doesn't have to be a class, explicitely. Sometimes, it can just be a set of utility functions, returned into a table. The end-user is not
supposed to instantiate from the module, but just use the functions. Example, SOR.

I will suggest taking a look at The Great vrld's libraries, The Great Kikito's librairies, The Great Bartbes' librairies for reference. Not that they are the best ones ever (oh-well, they are, indeed ;)) but they contain very clean code. Shameless plug, mine can also serve as reference. ;)
Pay attention at how module are written, how internal classes are defined, it might help.

I will also mention Class-Commons, that you can use to define classes in your code, so that the end-user will use your code with his favourite OOP library (though it has to be Class-commons compatible). For instance, the Great Vrld used it in HardonCollider. ;)
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Policies for writing Lua modules

Post by Inny »

richardperkins wrote:These articles were extremely helpful, and I thank you for that.

I have adopted two styles from this based on whether I want the "module" to be instantiated or not.

Inny, your technique intrigues me. How exactly do you implement your modules and/or what is the purpose or perk(s) to modelling in that way?
My style is, at core, this:

Code: Select all

local Module = {
  init = function(self) end
}
Module.__index = {
}
return Module
This is a completely stripped down Lua object suitable for being used as a metatable like this: local instance = setmetatable({}, Module); Module.init(instance)

When I mentioned entity-component, I mean I learned a few things from crafty.js and instead of inheriting between classes, the instances themselves are copies of their Component classes.

Code: Select all

local player = {}
for k, v in pairs(Sprite.__index) do
  if player[k] == nil then player[k] = v
end
Sprite.init(player);
Developed a little further, I can write code like so:

Code: Select all

local player = entity();
player:require(sprite)
That actually raises a good question, how do you guys like require as the verbiage for how an entity uses a component? some time addComponent is used, and in the javascript, extend is what you see commonly (like even in jquery they have $.extend).
User avatar
ejmr
Party member
Posts: 302
Joined: Fri Jun 01, 2012 7:45 am
Location: South Carolina, U.S.A.
Contact:

Re: Policies for writing Lua modules

Post by ejmr »

Thank you Roland for sharing the articles—great reads.
Inny wrote:That actually raises a good question, how do you guys like require as the verbiage for how an entity uses a component? some time addComponent is used, and in the javascript, extend is what you see commonly (like even in jquery they have $.extend).
This is bike-shedding, but personally I like ‘requires’ more than ‘require’ because I think of variables in code as if they were third-person pronouns in English grammar. Since ‘require’ already has a meaning in Lua maybe something like ‘uses’ or ‘includes’ would be better. Since you say, “…how an entity uses…,” that tips my preference towards ‘uses‘ as the method name.

But again, that is all just bike-shedding. Conceptually I like the idea a lot.
ejmr :: Programming and Game-Dev Blog, GitHub
南無妙法蓮華經
Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests