Page 1 of 3

clasp - tiny class library

Posted: Sun Apr 23, 2017 3:03 pm
by evölbug
Yes, yes I know, there are a ton of class libraries out there, some good, some bad, to each their own. None of them quite felt good to me, so I made my own. This is a simple and clean class library. It uses c-style syntax and, while I know this is Lua and some may scream at this heresy, I think it looks better than adhering to Lua's functionlike approach (although you can still use it).

It does simple singular inheritance and metamethods declaration in class (using a separate table). No super, instead you call superclass methods explicitly, passing self as first argument.

Here's the github link and usage: https://github.com/evolbug/lua-clasp
wiki link too: https://love2d.org/wiki/clasp

Code: Select all

class = require "clasp"

-- Basic class
Vector = class {
  isVector = true; -- static values
  init = function(self, x, y) -- initializer function
    self.x = x
    self.y = y
  end;
}
a = Vector(10, 10)
print('Vector:', a.x, a.y, a.isVector) -- "Vector: 10 10 true"

Code: Select all

-- Inheritance
Vector3 = Vector:extend {
  init = function(self, x, y, z) -- function overriding
    Vector.init(self, x, y) -- superclass method call
    self.z = z
  end;
}
b = Vector3(1, 2, 3)
print('Vector3:', b.x, b.y, b.z, b.isVector) -- "Vector3: 1 2 3 true"

Code: Select all

-- Metamethods
Point = class {
  init = function(self, x, y)
    self.x, self.y = x,y
  end;
  __ = { -- metamethod table
    tostring = function(self)
      return 'Point('..tostring(self.x)..', '..tostring(self.y)..')'
    end;
  };
}
c = Point(15, 25)
print(c) -- "Point(15, 25)"

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 6:16 pm
by airstruck
Not bad, looks similar to a solution I've used before (particularly "extend"). A few thoughts:

- Setting everything to weak keys seems suspicious/unnecessary, why do that?
- Calling pairs every time you instantiate a class is slow, can that be moved to declaration from instantiation?
- The js-style constructors that can return something other than the instance are interesting, be sure to document that behavior.
- Maybe add something like members = members or {} to also support the "not-c-style" more cleanly.

Regarding "pairs," you could get rid of it by using _meta as the metatable instead of copying everything over.

Code: Select all

local meta = self.__meta or {}
meta.__index = self

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 6:51 pm
by evölbug
- Setting everything to weak keys seems suspicious/unnecessary, why do that?
I saw some class library use weak keys, so I put them in too, dunno if it changes much really, removed it because keeps the code shorter
- Calling pairs every time you instantiate a class is slow, can that be moved to declaration from instantiation?
pairs() is called only for and if metamethods are defined in the class, and no it can't be moved, copying is necessary or it creates a metatable mess. haven't figured out how to do it without pairs yet, and why the mess is created otherwise
- The js-style constructors that can return something other than the instance are interesting, be sure to document that behavior.
they don't return anything, constructor - init() is called by `new` class system function and disregards it's return value. I noticed the problem in the code and fixed
- Maybe add something like members = members or {} to also support the "not-c-style" more cleanly.
added this one

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 7:05 pm
by evölbug
here's visible difference in using copying(current method) vs referencing (suggested) respectively
difference.png
difference.png (9.76 KiB) Viewed 7892 times
the problem makes itself apparent when inheriting which is shown above

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 7:07 pm
by airstruck
Not sure how that happened, I just tried this and it seems to work ok:

Code: Select all

return function (members)
   local cls = {}
   function cls.new (self,...)
      local meta = self.__meta or {}; meta.__index = self; self.__meta = nil -- this changed
      local object = setmetatable({}, meta)
      return object.init and object:init(...) and object or object
   end
   function cls.extend(base,members) return setmetatable(members,{__index=base,__call=cls.new}) end
   return setmetatable(members or {},{__index=cls,__call=cls.new})
end
Haven't inspected it closely though, just tested that it works with the __tostring example from the readme.

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 7:10 pm
by evölbug
well, it technically "works", but try using this dump method to check the resulting structure of an inheriting class

Code: Select all

function indent(depth)
   local s = ''
   for i=0,depth do s=s..'  ' end
   return s
end

function dump(table, label, depth)
   local depth = depth or -1
   local label = label or tostring(table)

   print(indent(depth)..label..' {')

   for k,v in pairs(table) do
      if type(v) == 'table' and depth<8 then
         dump(v, k, depth+1)
      elseif depth<8 then
         print(indent(depth+1)..k..': '..tostring(v))
      else
         return
      end
   end
   if getmetatable(table) then
      dump(getmetatable(table), '__meta__', depth+1)
   end

   print(indent(depth)..'}')
end

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 7:14 pm
by airstruck
I get the exact same structure... https://hastebin.com/movahoyisi.lua

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 7:17 pm
by evölbug
that's because you aren't inheriting

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 7:22 pm
by airstruck
Ahh, I see what you mean. Try meta = rawget(self, '__meta') or {}

Re: clasp - 13 lines of class

Posted: Sun Apr 23, 2017 7:29 pm
by evölbug
rawget() doesn't work at all, in fact it breaks metamethods