Page 1 of 1

class.lua and more

Posted: Mon May 23, 2016 5:30 pm
by Trebgarta
Yet another class implementation I guess, though this is not intended for public usage.

I just relearned Lua and jumped on Löve 3 days ago and this is the result. A class implementation for myself, for a ECS-like structure implementation in the file next to it.

What I'd love to have is some code review of the class.lua. Since it will be the staple if I build up.

Do whatever you want with the code and explore the rest of the repo if you'd like to.

Also I am thinking of rolling my own GUI system and I am writing an issue about it ATM. Input on topic would be appreciated too, since I cant say I am confident that I precisely know what a GUI is and what functionalities I'd have to put in.

Thanks in advance! :)

Re: class.lua and more

Posted: Mon May 23, 2016 5:54 pm
by ivan
Hello and welcome to the forums.
Ok, the code is not bad, in fact is quite readable.
Good job there, considering that you are new to Lua.
There are some things that should be pointed out:

Code: Select all

local function copy(self)
  local copied = {}
  for k, v in pairs(self) do copied[k] = v end
  return setmetatable(copied , getmetatable(self))
end
What you've done here is a shallow copy so:

Code: Select all

player = { position= { x=5,y=5 } }
player2 = copy(player)
Now player and player2 are sharing the same "position" table.
This may not seem like a big deal but you have to be aware of this when using "copy".
Personally, I wouldn't use shallow copies in this case since it may accidentally lead to hard to find bugs.

The "inherit" function looks a little strange.
Again, it uses the "shallow copy" technique which is not a big deal,
but my suggestion is to avoid copying/moving data unless you really have to.
Note that it is possible to setup inheritance via "setmetatable" and the "__index" operator
without having to copy stuff between tables.

Good luck!

PS. Here is the code I use for inheritance:

Code: Select all

local oo = {}

local meta = {}
local rmeta = {}

-- Creates a new class
function oo.class()
  local c = {}
  local mt = { __index = c }
  meta[c] = mt
  rmeta[mt] = c
  return c
end

-- Creates a subclass
function oo.subclass(p)
  assert(meta[p], "undefined parent class")
  local c = oo.class()
  return setmetatable(c, meta[p])
end

-- Creates a new instance
function oo.instance(c)
  assert(meta[c], "undefined class")
  return setmetatable({}, meta[c])
end

-- Gets the class of an instance
function oo.getclass(i)
  local mt = getmetatable(i)
  return rmeta[mt]
end

return oo
example usage:

Code: Select all

local oo = require 'oo'

local Fruit = oo.class()
Fruit.sweetness_threshold = 5 -- sort of like "static"
function Fruit:initialize(sweetness)
  self.sweetness = sweetness
end
function Fruit:isSweet()
  return self.sweetness > Fruit.sweetness_threshold
end

local Lemon = oo.subclass(Fruit) -- subclassing
function Lemon:initialize()
  Fruit.initialize(self, 1) -- manually invoking the superclass' initializer
end

local lemon = oo.instance(Lemon)
lemon:initialize()
print(lemon:isSweet()) -- false

Re: class.lua and more

Posted: Mon May 23, 2016 7:20 pm
by Trebgarta
Thanks! Exactly the reason why I posted this here. I couldn't ask for a better feedback.

I'm not exactly new to lua. I've been a little jumpy in my 1.5 years programming so I can say I used löve2d down the road somewhere but I cant recall when and for what. I definitely remember I didnt learn metatables until 3 days ago though.

Why do you recommend against moving/copying if its done right? I mean, I didn't do it right, but after I fix it, would you still suggest I scrap copying/moving and use, for example, __index for inheritance? I'd prefer leave __index free just in case.

Reading here now, I guess what I need it to do is to recursively move up child tables until values are found and make new tables and copy values. This was the idea behind the copy function too since

x = class()
y = x

had the problem of 2 reference 1 table, in the same sense. I shouldve thought of that. So what I need is that recursion, correct?

There is also this at the end of that file but this is quiet baffling at 400+ lines.

Re: class.lua and more

Posted: Tue May 24, 2016 2:59 pm
by Trebgarta
I guess it should be fixed now. Used recursive model from another gist, the nonrecursive gist didnt work for me. Couldn't index "stack". The gist I used is in the class file comments.

Also didnt get it quiet right yesterday, forgot to mention: My inheritance model allows classes inheriting other classes arbitrarily, in a multiple inheritance manner. If your implementation also has this I don't quiet see it.

Re: class.lua and more

Posted: Tue May 24, 2016 3:11 pm
by ivan
had the problem of 2 reference 1 table, in the same sense. I shouldve thought of that. So what I need is that recursion, correct?
If you want to make a 'deep copy' then you need something like recursion (or a queue).
But then you have to be careful with cyclic tables: a = {}; a.b=a
If your implementation also has this I don't quiet see it
No multiple inheritance in my example code.
Yes, it's possible to do it, but you'll find that the resulting complexity far outweighs the benefits.
would you still suggest I scrap copying/moving and use, for example, __index for inheritance?
Sure, the simpler the solution the better.
One of the strengths of metatables is that you don't have to copy/move stuff between tables.

Re: class.lua and more

Posted: Tue May 24, 2016 3:18 pm
by alloyed
It's worth noting that in a language as dynamic as lua inheritance is more a unit of code organization than "the" tool you use to build your classes. You can always do something like

Code: Select all

ClassA.method = ClassB.method
ClassA.otherMethod = ClassC.otherMethod
To share code between multiple classes if you think it makes the most sense that way.

Re: class.lua and more

Posted: Tue May 24, 2016 3:58 pm
by Trebgarta
ivan wrote:
If you want to make a 'deep copy' then you need something like recursion (or a queue).
But then you have to be careful with cyclic tables: a = {}; a.b=a

No multiple inheritance in my example code.
Yes, it's possible to do it, but you'll find that the resulting complexity far outweighs the benefits.

Sure, the simpler the solution the better.
One of the strengths of metatables is that you don't have to copy/move stuff between tables.
I think, while I agree with YAGNI and KISS are good principals, it didnt took me much effort to do these, and still it is a simple personal project so I kinda can do what I want :P

Worst case scenario I believe switching to a metatable implementation or an external library later wouldn't be extremely difficult.

Nevertheless what I will do next is to look into hump.class since I already have it, and decide on whether switching to that or scrapping the good sides of it to implement in my implementation, an example might eventually be using __index but I need to look into it.

Mine is already inspired by it but shallowly, I havent read hump.class throughly, just studied the interface at the docs, not the source.
alloyed wrote:It's worth noting that in a language as dynamic as lua inheritance is more a unit of code organization than "the" tool you use to build your classes. You can always do something like

Code: Select all

ClassA.method = ClassB.method
ClassA.otherMethod = ClassC.otherMethod
To share code between multiple classes if you think it makes the most sense that way.
Of course, its preference I suppose.

This isn't the only way of doing inheritance; we could make __index a function which explicitly tries to look a function up in the base class(es). But this method will give better performance, at a cost of making the class objects somewhat fatter.
I wonder how true thisis

Re: class.lua and more

Posted: Tue May 24, 2016 4:20 pm
by kikito
In the current iteration of middleclass (after several rewrites etc) what happens is: classes copy the methods from their superclasses, with a (somewhat complex) loop, which sometimes changes all the child classes of a class. Instances don't copy methods - they just have a metatable with an __index field.

This is optimized for the usual case, which is that classes are declared rarely (so creation speed is not a concern), and instances can be created frequently (so a faster index-based method is prefearable). It also happens to be more intuitive for handling inherited metamethods and also a nice place to include a custom __index metamethod, if you want to include one.

Re: class.lua and more

Posted: Tue May 24, 2016 4:24 pm
by ivan
vrld's stuff is usually pretty neat, so you're in good hands with hump.
middleclass is great too, but in practice you're probably not going to use a lot of its features.
But this method will give better performance, at a cost of making the class objects somewhat fatter
There's is no "correct" way to write OO in Lua, it's more about what works for you.

Re: class.lua and more

Posted: Tue May 24, 2016 6:31 pm
by Trebgarta
I guess I will opt out of rolling my own stuff. It was a good learning experience while it lasted. My main motivation in a class implementation was to build upon it to write an ECS but I'm now not so sure of that either.

I'll use hump.class instead. How is tiny-ecs, and what would be a good GUI library btw?