Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- eoo.lua: Edrx'x simple OO scheme. -- This file: -- http://angg.twu.net/dednat5/eoo.lua.html -- http://angg.twu.net/dednat5/eoo.lua -- (find-dn5 "eoo.lua") -- (find-tkdiff "~/blogme4/eoo.lua" "~/dednat5/eoo.lua") -- Author: Eduardo Ochs <eduardoochs@gmail.com> -- Version: 2011nov21 -- License: GPL3 -- -- A very simple object system. -- The metatable of each object points to its class, -- and classes are callable, and act as creators. -- New classes can be created with, e.g.: -- Circle = Class { type = "Circle", __index = {...} } -- then: -- Circle {size = 1} -- sets the metatable of the table {size = 1} to Circle, -- and returns the table {size = 1} (with its mt modified). -- -- Originally from: (find-angg "LUA/canvas2.lua" "Class") -- A tool: (find-angg ".emacs.templates" "class") -- «.test-eoo» (to "test-eoo") -- «.box-diagram» (to "box-diagram") Class = { type = "Class", __call = function (class, o) return setmetatable(o, class) end, } setmetatable(Class, Class) otype = function (o) -- works like type, except on my "objects" local mt = getmetatable(o) return mt and mt.type or type(o) end -- Code for inheritance (2011nov21), untested... -- The examples of usage for this are coming soon! over = function (uppertable) return function (lowertable) setmetatable(uppertable, {__index=lowertable}) return uppertable end end ClassOver = function (upperclassmt) return function (lowerclass) setmetatable(upperclassmt.__index, {__index=lowerclass.__index}) return Class(upperclassmt) end end -- dump-to: tests -- «test-eoo» (to ".test-eoo") --[==[ -- Here is a detailed explanation of how this works. -- Using the notation of the __mt patch, at: -- http://angg.twu.net/__mt.html -- (find-TH "__mt") -- we can rewrite the code above as: -- Class = { type = "Class", -- __call = \ (class, o) => o.__mt = class end } -- Class.__mt = Class -- otype = \ (o) local mt = o.__mt; => mt and mt.type or type(o) end -- Here is a test for it (note: it does _not_ require a patched Lua). * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) ee_dofile "~/dednat5/eoo.lua" -- this file Vector = Class { type = "Vector", __add = function (V, W) return Vector {V[1]+W[1], V[2]+W[2]} end, __tostring = function (V) return "("..V[1]..","..V[2]..")" end, __index = { norm = function (V) return math.sqrt(V[1]^2 + V[2]^2) end, }, } v = Vector {3, 4} -- v = { 3, 4, __mt = Vector} w = Vector {20, 30} -- w = {20, 30, __mt = Vector} print(v) --> (3,4) print(v + w) --> (23,34) print(v:norm()) --> 5 print( type(v)) --> table print(otype(v)) --> Vector print( type("")) --> string print(otype("")) --> string -- So we have: -- -- Class = { -- type = "Class", -- __call = \(class, o) => o.__mt = class end, -- __mt = Class -- } -- Vector = { -- type = "Vector", -- __add = \(V, W) => {V[1]+W[1], V[2]+W[2]} end, -- __tostring = \(V) => "("..V[1]..","..V[2]..")" end, -- __index = { norm = \(V) => math.sqrt(V[1]^2+V[2]^2) end }, -- __mt = Class -- } -- -- and we can use reductions to understand "Vector {3, 4}" and "v:norm()": -- -- v = Vector {3, 4} -- --~-> Vector({3, 4}) -- --~-> Vector.__mt.__call(Vector, {3, 4}) -- --~-> Class.__call(Vector, {3, 4}) -- --~-> (\(class, o) => o.__mt = class end)(Vector, {3, 4}) -- --~-> (\() => {3, 4}.__mt = Vector end)() -- --~-> {3, 4, __mt = Vector} -- -- and: -- -- v:norm() -- --~-> {3, 4, __mt=Vector}:norm() -- --~-> {3, 4, __mt=Vector}.norm ({3, 4, __mt=Vector}) -- --~-> {3, 4, __mt=Vector}.__mt.__index.norm({3, 4, __mt=Vector}) -- --~-> Vector.__index.norm({3, 4, __mt=Vector}) -- --~-> (\(V) => math.sqrt(V[1]^2+V[2]^2) end)({3, 4, __mt=Vector}) -- --~-> (\() => math.sqrt( 3^2+ 4^2) end)() -- --~-> (\() => 5 end)() -- --~-> 5 -- «box-diagram» (to ".box-diagram") -- Here is a box diagram that explains more clearly what we get after -- running "v = Vector {3, 4}; w = Vector {20, 30}". -- -- <fcal> = function (class, o) return setmetatable(o, class) end -- <fadd> = function (V, W) return Vector {V[1]+W[1], V[2]+W[2]} end -- <ftos> = function (V) return "("..V[1]..","..V[2]..")" end, -- <fnor> = function (V) return math.sqrt(V[1]^2 + V[2]^2) end -- -- /---+---\ /---+----\ -- v = | 1 : 3 | w = | 1 : 20 | -- | 2 : 4 | | 2 : 30 | -- \mt-+---/ \mt-+----/ -- : ............/ -- : / -- vv -- /--------------+----------\ -- Vector = | "type" : "Vector" | -- | "__add" : <fadd> | -- | "__tostring" : <ftos> | /--------+--------\ -- | "__index" : * .......> | "norm" : <fnor> | -- \mt------------+----------/ \--------+--------/ -- : -- v -- /----------+---------\ -- Class = | "type" : "Class" | -- | "__call" : <fcal> | -- \mt--------+---------/ -- : ^ -- \..../ -- -- If we define precisely what is a "Class box" we can represent the -- main part of the diagram above more compactly, as: -- -- /---+---\ /---+----\ -- v = | 1 : 3 | w = | 1 : 20 | -- | 2 : 4 | | 2 : 30 | -- \mt-+---/ \mt-+----/ -- : ............/ -- : / -- vv -- /-Class--------+----------\ -- Vector = | "type" : "Vector" | -- | "__add" : <fadd> | -- | "__tostring" : <ftos> | -- +--------------+----------| -- | "norm" : <fnor> | -- \--------------+----------/ -- --]==] -- Local Variables: -- coding: raw-text-unix -- ee-anchor-format: "«%s»" -- End: