Class extension

Hi. I’ve extended the class implementation provided by codea to support (even in a rough way) also interfaces implementation. I was looking for something similar to what AS3 allows to do, having no multiple inheritance but having single inheritance plus multiple interfaces implementation. The code is based on a ClassLib.lua I founded on lua-users.org.

-- Class.lua
-- Compatible with Lua 5.1 (not 5.0).

--[[ 
class(ancestor, interfaces...)

examples:
- class(base) 
inherits from base, so is_a(base) is true

class(base, interface1,..,interfacen)
inheriths from base and implements all the interfaces, so
is_a is true only for base, while implements is true for all the interfaces, and also for base


class(nil, interface1,..,interfacen)

implements interfaces but without inheritance, so is_a is true only for self name and i plements is true for all the interfaces

---

pay attention to symbol redefinition. in init phase a warning is logged when a symbol is redefined but no more
--]]

local reserved =
{
    __index          = true,
    _base            = true,
    is_a               = true,
    implements   = true,
    init                 = true
}

function class(...)
    
    local c = {}    -- a new class instance

    local args = {...}
    if table.getn(args) then
        
        local base = args and args[1] or nil
        
        if type(base) == 'table' then
            -- our new class is a shallow copy of the base class!
            for i,v in pairs(base) do
                c[i] = v
            end
            c._base = base
        end
        
        table.remove(args,1)
        
        for _,i in pairs(args) do
            if type(i) =='table' then
                for k,v in pairs(i) do
                    if not reserved[k] and type(i[k]) == 'function' then
                        if c[k] then
                            print("warning " .. k .. 
                                " is already defined")
                        end
                        c[k] = v
                    end
                end
            end
        end
    end    

    -- the class will be the metatable for all its objects,
    -- and they will look up their methods in it.
    c.__index = c

    -- expose a constructor which can be called by <classname>( <args> )
    local mt = {}
    mt.__call = function(class_tbl, ...)
        local obj = {}
        setmetatable(obj,c)
        if class_tbl.init then
            class_tbl.init(obj,...)
        else 
            -- make sure that any stuff from the base class is 
            --initialized!
            if base and base.init then
                base.init(obj, ...)
            end
        end

        return obj
    end

    c.is_a = function(self, klass)
        local m = getmetatable(self)
        while m do 
            if m == klass then return true end
            m = m._base
        end
        return false
    end
    
    c.implements = function(self, interface)
            -- Check we have all the target's callables
        for k, v in pairs(interface) do
            if not reserved[k] and type(v) == 'function' and 
                type(self[k]) ~= 'function' then
                return false
            end
        end
        return true
    end

    setmetatable(c, mt)
    return c
end

Single inheritance with multiple interfaces is quite good! It’s like Java. Have you tested this? Hope this would be accepted by TLL. More advance OO feature is nice. :slight_smile:

Think you, @shrike. Your approach has jogged my memory and made me do a big forehead slap. There’s a good use for a similiar implementation in my current project. Should actually simplify some of the copy, copy, copy hole I’ve dug myself.

You should check out MiddleClass, which @JockM mentioned in this thread:

http://twolivesleft.com/Codea/Talk/discussion/1057/advanced-class-features#Item_9

It has support for mixins, a powerful way to add interfaces to classes.

@shrike Very nice! You might want to take a look at MiddleClass if you haven’t. There are some nice ideas in there you might want to borrow — such as mixins and the ability to address the superclass.

Hi @bee, yes I’ve tried it and It seems to work as I expected. Just adding that to a project let you override basic class() definition, without changing at all the original behaviour (consider that the most of the code IS the original source code). It just adds functionalities if any interfaces is declared and anyway without affecting the inehritance (at least until there’s no collision of names).

I would like to do some more little changes and improvement but for now I had just one specific need and it’s addressed, so I will do that later. Thanks @toadkick and @JockM, I’ll sure give a look at MiddleClass implementation, it seems very interesting!