Allow multiple inheritance objects defined at clone time
authorChristian Thaeter <ct@pipapo.org>
Mon, 6 Mar 2017 15:19:24 +0000 (16:19 +0100)
committerChristian Thaeter <ct@pipapo.org>
Mon, 6 Mar 2017 15:19:24 +0000 (16:19 +0100)
proto.lua
prototest.lua

index 7ffa3fa..6ec8da6 100644 (file)
--- a/proto.lua
+++ b/proto.lua
@@ -1,11 +1,12 @@
 --- Prototype based Objectsysten
 ---   Objects are `cloned` from existing objects ,`Object` is the mother of all objects.
----   One can introduce additional inheritance paths with 'inherit'. Lookup order is
+---   One can introduce additional inheritance paths. Lookup order is
 ---   first the prototype chain from `clone` and then all parent chains in the order
 ---   they where added.
 ---
 ---   Multiple inheritance
----     Objects can have an ordered list of other objects where they inhterit from
+---     Apart from the object where cloned from, objects can have ordered multiple
+---     inheritance. Parents can be appended dynamically at later time.
 ---
 ---   Differential inheritance
 ---     Dynamic changes in prototypes and parent objects are reflected in children unless
 ---   Member lookup is child first then for each parent depth first.
 ---
 ---
----
 
 
 -- Object is the root object for all objects to be created
 local Object = {}
 setmetatable(Object, Object)
 
+local function indexfn (self, key)
+    local erased = rawget(self,"__proto_erased")
+    if erased and erased[key] then
+        return nil
+    end
+
+    local parents = rawget(self,"__proto_parents") or {}
+    for i=1,#parents do
+        local value = parents[i][key]
+        if value then
+            return value
+        end
+    end
+end
 
 --- Object:proto_clone ()
 --- Object:proto_clone {...}
----   takes an optional table for new mebers of the cloned object
----   Sole strings on this table (aka, the integer part) define which members to copy up
+---   takes an optional table for constructing the cloned object
+---     - associative mebers define new slots
+---     - Objects define objects to inherit from
+---     - Strings define members to copy up
 function Object:proto_clone(newobject)
     assert(not newobject or type(newobject) == 'table')
     newobject = newobject or {}
@@ -34,8 +50,20 @@ function Object:proto_clone(newobject)
 
     -- copy up members
     for i=1,#newobject do
-        assert(type(newobject[i]) == 'string')
-        newobject[newobject[i]] = self[newobject[i]]
+        if type(newobject[i]) == 'string' then
+            newobject[newobject[i]] = self[newobject[i]]
+        elseif type(newobject[i]) == 'table' then
+            local parents = rawget(newobject, "__proto_parents")
+
+            if not parents then
+                parents = {self}
+                rawset(newobject, "__proto_parents",  parents)
+                newobject.__index = indexfn
+            end
+
+            assert((pcall(function () return newobject[i].indexable end)), "object not indexable")
+            parents[#parents + 1] = newobject[i]
+        end
         newobject[i] = nil
     end
 
@@ -43,21 +71,6 @@ function Object:proto_clone(newobject)
 end
 
 
-local function indexfn (self, key)
-    local erased = rawget(self,"__proto_erased")
-    if erased and erased[key] then
-        return nil
-    end
-
-    local parents = rawget(self,"__proto_parents") or {}
-    for i=1,#parents do
-        local value = parents[i][key]
-        if value then
-            return value
-        end
-    end
-end
-
 
 --- Object:proto_erase (name)
 ---   marks member 'name' as delered, removed from inheritance
@@ -80,16 +93,17 @@ end
 function Object:proto_inherit (...)
     local args = {...}
     if #args > 0 then
-        local parents = rawget(self,"__proto_parents") or rawset(self,"__proto_parents", {self.__index} or {}) and rawget(self,"__proto_parents")
+        local parents = rawget(self,"__proto_parents")
+        if not parents then
+            parents = {self.__index}
+            rawset(self, "__proto_parents",  parents)
+            self.__index = indexfn
+        end
 
         for i=1,#args do
             assert((pcall(function () return args[i].indexable end)), "object not indexable")
             parents[#parents + 1] = args[i]
         end
-
-        if type(self.__index) == 'table' then
-            self.__index = indexfn
-        end
     end
     return self
 end
index ac877b5..4b70128 100644 (file)
@@ -173,3 +173,28 @@ local Duck = Waterfowl:proto_clone {
 
 print(Duck:proto_pcall('canQuack'))
 
+
+-- multiple inheritance
+
+local A = Object:proto_clone {
+  sayName = function (self) print(self.myName) end
+}
+
+local B = Object:proto_clone {
+  myName = "I am B"
+}
+
+local AB1 = Object:proto_clone ()
+AB1:proto_inherit (A,B)
+AB1:sayName()
+
+local AB1_1 = Object:proto_clone {A,B}
+AB1_1:sayName()
+
+local AB2 = A:proto_clone ()
+AB2:proto_inherit (B)
+AB2:sayName()
+
+local AB3 = B:proto_clone ()
+AB3:proto_inherit (A)
+AB3:sayName()