## clasp - tiny class library

evölbug
Prole
Posts: 38
Joined: Wed Dec 21, 2016 12:58 pm
Contact:

### Re: clasp - 8 lines of class

Reading metamethods directly from class will break their inheritance on longer chains, because pairs() doesn't read the __index tree, which is mandatory for them to be inherited correctly. Of course I could manually check for each individual metamethod to trigger __index lookup, but that'd expand the code unnecessarily, that's why I use a table, I don't think it's too much of a bother really. I'll see what I can do to move the metamethod copying to declaration. Also I've already said what I think about cycles, they will make things incredibly hard to debug if the class structure itself turns out to be flawed.

evölbug
Prole
Posts: 38
Joined: Wed Dec 21, 2016 12:58 pm
Contact:

### Re: clasp - 8 lines of class

I'm not that concerned about performance as it appears to be a bit faster than middleclass according to its performance test. Now, it isn't a real world scenario so I don't know if it's actually faster on heavily packed classes.
perf.png (13.95 KiB) Viewed 3690 times

airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

### Re: clasp - 8 lines of class

evölbug wrote:Reading metamethods directly from class will break their inheritance on longer chains, because pairs() doesn't read the __index tree, which is mandatory for them to be inherited correctly
That's why I'm suggesting copying them from the parent class to the subclass in "extend." The proto.__index=proto thing facilitates that (may not be necessary, but I think you'd be back to closures).
Also I've already said what I think about cycles, they will make things incredibly hard to debug if the class structure itself turns out to be flawed.
I generally agree that proto.__index=proto is annoying, and possibly more difficult to debug, but if the alternative is copying a bunch of stuff with pairs every time an object is instantiated that's pretty much a non-starter.

evölbug
Prole
Posts: 38
Joined: Wed Dec 21, 2016 12:58 pm
Contact:

### Re: clasp - 8 lines of class

It's not so slow that I'd give up the readability of objects for a few nanoseconds of performance. Parsing the whole class structure and copying down everything on inheritance is not much better, even if it only happens once in declaration. Just 3 levels of inheritance creates an unreadable cyclic fractal that I don't want, and it grows exponentially, making it impossible to read subclasses deeper than 2 levels, even if you try to ignore cycles (which can't be done reliably because it would involve caching and ignoring values, and you would end up with only half of the structure displayed - slow, messy and you can't reliably see the whole structure).

Copying only happens within the confines of that one table, and it's only ever going to be a few elements unless you start defining things other than metamethods, besides it creates clean classes and instances in the end.

I tried moving metamethod copying outside of instance creation anyway, but I haven't figured it how to do it decently yet.

bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

### Re: clasp - 8 lines of class

raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

### Re: clasp - 8 lines of class

Classes is arguably the single heaviest used code, it's gotta be fast, period. If you need debugging version, make a separate one just for that.

Also you really need to test things like that. You can't just assume that it's faster or slower to do things certain way.

Finally, there is a saying, "he who laughs at ounces cries over pounds". It may not look like much but it all adds up.

airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

### Re: clasp - 8 lines of class

evölbug wrote:I tried moving metamethod copying outside of instance creation anyway, but I haven't figured it how to do it decently yet.
If by "decently" you mean "without cycles," why not move back to closed-over functions? You didn't seem to mind it before, this time you can have it use an upvalue to legitimize its existence.

Code: Select all

local base = { init = function()end }
function base:extend (proto)
proto = proto or {}
local meta = { __index = proto }
for k, v in pairs(self) do if k:match '^__' and proto[k] == nil then proto[k] = v end end
for k, v in pairs(proto) do if k:match '^__' then meta[k] = v end end
return setmetatable(proto, { __index = self, __call = function (_, ...)
local object = setmetatable({}, meta)
return object, object:init(...)
end })
end
return setmetatable(base, { __call = base.extend })
This stuff shouldn't be that hard; I don't know if you posted here to get feedback or just to announce it, but as you're aware you haven't figured out how to "do it decently" yet, it would be good to let people know that it's not ready for actual use.

Some unit tests would also be good for ensuring that it does what you think it does, and so that alternatives can be tested for compliance with however it's supposed to work.

evölbug
Prole
Posts: 38
Joined: Wed Dec 21, 2016 12:58 pm
Contact:

### Re: clasp - 8 lines of class

I posted it for both feedback and announcement, and really thanks for all the suggestions and inspiration.
but as you're aware you haven't figured out how to "do it decently" yet, it would be good to let people know that it's not ready for actual use.
Well, it was/is in theory ready for use, maybe some updates were needed here and there, but, if it wasn't considered ready before, I'm fairly confident the new iteration is final.

I forgot about upvalues as I am not that familiar with the concept coming from other languages that don't have them, but I optimized your code and applied it to my previous style. I still didn't like parsing the whole class and copying down metamethods to each new subclass, as it can and most will pull any unwanted "private" data (assuming "double underscore" style for "strictly private") into metatables; it's also making subclasses larger by having duplicate methods, and also a little bit slower declaration.

This is the new and final iteration unless I or someone here finds an optimization that can be done. 2 lines less than old code.

Code: Select all

local base = { init = function()end; extend = function(self, proto)
local objectmeta = {__index = proto}
local proto = setmetatable(proto or {},{__index=self, __call=function(_, ...) local object=setmetatable({},objectmeta) return object,object:init(...) end})
for k,v in pairs(proto.__ or {}) do objectmeta['__'..k]=v end
return proto end }
return setmetatable(base, { __call = base.extend })
Some unit tests would also be good for ensuring that it does what you think it does, and so that alternatives can be tested for compliance with however it's supposed to work.
Indeed, but it's not easy writing tests that can be tested against other class frameworks and test things thoroughly and accurately enough. I will try though!
Object difference between new and old clasp
newclasp.png (12.03 KiB) Viewed 3591 times

airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

### Re: clasp - 8 lines of class

This won't work if proto isn't passed in, because objectmeta's __index will be nil.

Code: Select all

    local objectmeta = {__index = proto}
local proto = setmetatable(proto or {}, -- ...

This is why tests are important. You can use these if you want, it's very similar, just change 'constructor' to 'init' and add tests for the metamethod stuff. The script to run the tests is here (test.lua); run lua test.lua clasp-test.lua.

evölbug
Prole
Posts: 38
Joined: Wed Dec 21, 2016 12:58 pm
Contact:

### Re: clasp - 8 lines of class

oh, missed about the proto, fixed it and clumped the lines more, now it's 4

thanks for the tests!

### Who is online

Users browsing this forum: 4vZEROv and 40 guests