Help request: class, inheritance and elegance

Hello everyone!
I have come to write the following code quite often (it is just an example), but i am not happy of it:

Button = class(IconBox)

Button.updateStateIcon = IconBox.updateState
function Button:updateState(data)
    self:updateStateIcon(data)
    local data = data or {}
    if data.infoVisible ~= nil then self:displayInfo( data.infoVisible ) end
end

Let me explain: i have many classes, build on top of each other.
I add new functionnalities to the new class, and i want to reuse the available ones.
However, for clarity and modularity, i want to use the same names for functions in all the classes.
In this example, the function is updateState.
So i want to reuse the updateState from IconBox in its derived class Button, add functionnalities, and still use the same name.
The only way i found to do it is this ugly Button.updateStateIcon = IconBox.updateState line.
So here is my question: Is there any more elegant way do achieve this?

Button = class(IconBox)

function Button:updateState(data)
    IconBox.updateState(self,data)
    ...
end

nop ?

Thanks for the suggestion.
But this not really,‘elegant’, is it?

The problem is that Button already knows it is a class drived from IconBox, so i dont want to write that down again. It would be very clean if i accepted to change the function name, but i want to keep the same one…

That would be ‘elegant’:

Button = class(IconBox)

function Button:updateState2(data)
    self:updateState(data)
    ...
end

But how to avoid this ‘2’ suffix?

I don’t know if. But it’s how I understand class inheritance, and I don’t know if my understanding of it is good :slight_smile:

I prefer that :slight_smile:

Button = class(IconBox)

function Button:updateState(data)
    super:updateState(data)
    ...
end

don’t forget the " : " function call is just syntax sugar, when you write myObj:method(data), it is myObj.method(myObj,data) under the hood.

THAT is elegant! Thanks i’ll try it. I didnt know this ‘super’ keyword! Thanks a lot

Hmm… Codea classes copy everything from the superclass to the new class, but it doesn’t support :, only .

Maybe you could, like IconBox.updateState, have a static function that would be copied?

IconBox = class()

IconBox.updateState = 0

IconBox.setUpdateState = function(state)
    IconBox.updateState = state
end

 -- Then all the normal code...
function IconBox:init()
    -- ...
end

Button = class(IconBox)

Button.updateStateIcon = IconBox.updateState
function Button:updateStateIcon(data)
    Button.setUpdateState(data)
    local data = data or {}
    --if data.infoVisible ~= nil then self:displayInfo( data.infoVisible ) end
end

Tested

Ugh… It’s like static voids and ints etc. in Java all over again…

@toffer this super keyword is unknown from my codea… Maybe another syntax?
@skythecoder it is not clear to me what is the improvement you propose?

@Jmv38 Ah, there are supers in Java, didn’t know about them in Codea. And if you look at my code, you’ll see it calls Button.setUpdateState(), which is never defined from within Button, but inherited from IconBox, because it uses . instead of :

I found the equivalent of super in Codea:

    self._base.updateState(self,data)

it is not very elegnat, though…

No no, there is no super in lua, that was just to reply Jvm’s ‘That would be elegant’. sorry for the confusion.

I wrote that. This is not exactly what i wanted, but close:

function Box:super(funcName,...)
    self._base[funcName](self,...)
end

self:super("updateStyleSpecific",data)

Unfortunately my formulation above works with the 1rst level of derived class, but when i use it in a 2nd level, class derived from a class derived from a class, i get a stackoverflow error. @Toadkick any suggestion? I am sure you’ve already considered and solved this question?

@SkyTheCoder my attemp to simulate super keyword:

super = setmetatable({},{
    __index = function(tbl,k)
        local _,v = debug.getlocal(2,1)
        return v._base[k]
    end
})

function setup()
    A = class()
    function A:fn()
        print("A.fn called")
    end
    B = class(A)
    function B:fn()
        print("call super.fn")
        super:fn()
    end
    b = B()
    b:fn()
end

Thanks a lot @toffer.
Unfortunately your solution is also a stck overflow at level2:

-- test super

super = setmetatable({},{
    __index = function(tbl,k)
        local _,v = debug.getlocal(2,1)
        return v._base[k]
    end
})
    A = class()
    function A:fn()
        print("A.fn called")
    end
    B = class(A)
    function B:fn()
        print("call super.fn")
        super:fn()
    end
    C = class(B)
    function C:fn()
        print("call super.fn")
        super:fn()
    end
function setup()

    b = B()
    b:fn()
    c=C()
    c:fn()
    
end

@Jmv38 - nvm, I’m totaly wrong with that, the scope is not passed correctly.
this one should be better:

-- test super

super = setmetatable({},{
    __index = function(tbl,k)
        local fn = debug.getinfo(2).func
        local _,scope = debug.getlocal(2,1)
        local base = scope
        repeat
            base = base._base
        until base[k] ~= fn
        return function (...)
            base[k](scope,unpack({select(2,...)}))
        end
    end
})

    A = class()
    function A:fn()
        print("A.fn called ",self)
    end
    B = class(A)
    function B:fn()
        print("B.fn called ",self)
        super:fn()
    end
    C = class(B)
    function C:fn()
        print("C.fn called ",self)
        super:fn()
    end

function setup()

    b = B()
    print("b", b)
    b:fn()
    c=C()
    print("c", c)
    c:fn()
    
end

[EDIT] Wow! Thanks a lot! I seemed to work, and i decided to measure the performance because debug is not supposed to be fast. So i did the following changes, where the print() statements where the slowest. So i commentedt out the print() and asked for a return value. And then i saw the returned values are nil. So it still doesnt work, sorry… Any idea?


-- test super

super = setmetatable({},{
    __index = function(tbl,k)
        local _,scope = debug.getlocal(2,1)
        local ctx,base = debug.getinfo(2,"nf"),c
        local name,fn = ctx.name,ctx.func        
        local base = scope
        while base[name] ~= fn do
            base = base._base
        end
        base = base._base
        return function (...)
            base[k](scope,unpack({select(2,...)}))
        end
    end
})

    A = class()
    function A:fn()
--        print("A.fn called ",self)
        return "A.fn called "
    end
    
    B = class(A)
    function B:fn()
--        print("B.fn called ",self)
        return super:fn()
    end
    
    C = class(B)
    function C:fn()
--        print("C.fn called ",self)
        return super:fn()
    end

function setup()

    local a = A()
    print("a", a)
    local str = a:fn()
    print(str)
    
    print("----------")
    
    local b = B()
    print("b", b)
    local t0 = os.clock()
    local str = b:fn()
    local t1 = os.clock()
    print(str)
    print(t1-t0)
    
    print("----------")
    
    local c=C()
    print("c", c)
    t0 = os.clock()
    str = c:fn()
    t1 = os.clock()
    print(str)
    print(t1-t0)

end

miss to return the applyed super method, nop ?

return function (...)
   return base[k](scope,unpack({select(2,...)}))
end

Also, I think you should really not consider this method for performance, I figured you’ve asked for more elegant way to call super class method.