Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- eoo.lua: Edrx'x simple OO scheme, with explanations.
-- This file:
--   http://angg.twu.net/dednat6/dednat6/eoo.lua.html
--   http://angg.twu.net/dednat6/dednat6/eoo.lua
--           (find-angg "dednat6/dednat6/eoo.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2019feb22
-- License: GPL3
--
-- A very simple object system.
-- The metatable of each object points to its class;
-- 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).
-- See the box diagrams below.
--
-- Originally from: (find-angg "LUA/canvas2.lua"  "Class")
-- A tool:          (find-angg ".emacs.templates" "class")

-- «.Class»		(to "Class")
-- «.otype»		(to "otype")

-- «.__mt»		(to "__mt")
-- «.__mt-box»		(to "__mt-box")
-- «.lambda»		(to "lambda")
-- «.Class-short»	(to "Class-short")
-- «.Vector»		(to "Vector")
-- «.Vector-box»	(to "Vector-box")
-- «.Vector-reductions»	(to "Vector-reductions")
-- «.Class-boxes»	(to "Class-boxes")



-- «Class» (to ".Class")
-- The code for "Class" is just these five lines.
Class = {
    type   = "Class",
    __call = function (class, o) return setmetatable(o, class) end,
  }
setmetatable(Class, Class)


-- «otype» (to ".otype")
-- This is useful sometimes.
-- "otype(o)" works like "type(o)", except on my "objects".
otype = function (o)
    local  mt = getmetatable(o)
    return mt and mt.type or type(o)
  end


-- «over» (to ".over")
-- Code for inheritance.
-- Note: I have used this only a handful of times!
-- See: (find-dn6 "diagstacks.lua" "MetaStack")
--      (find-dn6 "diagstacks.lua" "MetaStack" "MetaStack = ClassOver(Stack) {")
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




-- The first section was adapted from:
--   http://angg.twu.net/__mt.html
--             (find-TH "__mt")
--[[


  «__mt» (to ".__mt")

1. The __mt notation
====================
Metatables are explained in detail in section 2.8 of the Lua manual,

  (find-lua51manual "#2.8" "Metatables")
  (find-lua51manual "#pdf-setmetatable")
  
but I found out that a certain abuse of language - writing "A.__mt"
for the metatable of A - simplifies a lot the description of what
happens in the cases where the metatables are used.

Let's use the squiggly arrow, "-~->", for _rewritings_. The three
rewritings below are standard:

                     T.__foo  -~->  T["foo"]
                foo:bar(...)  -~->  foo.bar(foo, ...)
  function foo (...) ... end  -~->  foo = function (...) ... end

They are described in these sections of the manual:

  (find-lua51manual "#2.2"   "a.name")
  (find-lua51manual "#2.3"   "var.Name")
  (find-lua51manual "#2.5.8" "v:name(args)")
  (find-lua51manual "#2.5.9" "function f () body end")

If A and B are tables having the same non-nil metatable, then, modulo
details (error conditions, etc), we have:

  A + B        -~->  A.__mt.__add(A, B)
  A - B        -~->  A.__mt.__sub(A, B)
  A * B        -~->  A.__mt.__mul(A, B)
  A / B        -~->  A.__mt.__div(A, B)
  A % B        -~->  A.__mt.__mod(A, B)
  A ^ B        -~->  A.__mt.__pow(A, B)
  - A          -~->  A.__mt.__unm(A, A)
  A .. B       -~->  A.__mt.__concat(A, B)
  #A           -~->  A.__mt.__len(A, A)
  A == B       -~->  A.__mt.__eq(A, B)
  A ~= B       -~->  not (A == B)
  A < B        -~->  A.__mt.__lt(A, B)
  A <= B       -~->  A.__mt.__le(A, B)
                     (or not (B < A) in the absence of __le)
  A[k]         -~->  A.__mt.__index(A, k)
  A[k] = v     -~->  A.__mt.__newindex(A, k, v)
  A(...)       -~->  A.__mt.__call(A, ...)
  tostring(A)  -~->  A.__mt.__tostring(A)

Note that the listing above is a translation to the __mt notation of
the _cases involving metatables_ that are mentioned in these sections
of the manual:

  (find-lua51manual "#2.8" "op1 + op2")
  (find-lua51manual "#pdf-tostring")

and we follow the order in which these cases are listed in the manual.




 «__mt-box» (to ".__mt-box")

2. The __mt notation in box diagrams
====================================
We can depict the values of a and b after these assignments

  a = {10, 20, 30}
  b = {11, a, "foo", print}

as:

      /---+---------\         /---+----\
  b = | 1 :    11   |     a = | 1 : 10 |
      | 2 :     * ----------> | 2 : 20 |
      | 3 :  "foo"  |         | 3 : 30 |
      | 4 : <print> |         \---+----/
      \---+---------/

and if after that we do this, using the __mt notation above,

  T = {__index = b}
  a.__mt = T

we will have:

      /---+---------\         /---+----\
  b = | 1 :    11   |     a = | 1 : 10 |
      | 2 :     * ----------> | 2 : 20 |
      | 3 :  "foo"  |         | 3 : 30 |
      | 4 : <print> |         \mt-+----/
      \---+---------/           |
           ^                    v
           |                 /-----------+----\
           |             T = | "__index" : * -----\
           |                 \-----------+----/   |
           |                                      |
           \--------------------------------------/

The pointer to the metatable is represented as a "mt" at the floor
instead of as a (key, value) pair/line inside the box, Compare:

      /---+----\                     /--------+----\
  a = | 1 : 10 |	 	 a = |    1   : 10 |
      | 2 : 20 |   instead of: 	     |    2   : 20 |
      | 3 : 30 |		     |    3   : 30 |
      \mt-+----/		     | "__mt" :  * -----> T
        |                            \--------+----/
        \----> T

Using these diagrams it is easy to calculate a[4] by a series of
reductions. Without metatables we would have a[4] = nil, so:

  a[4]  -~->  a.__mt.__index[4]
        -~->       T.__index[4]
        -~->               b[4]
        -~->              print




 «lambda» (to ".lambda")

3. A lambda notation for Lua functions
======================================
Gavin Wraith implemented two shorthands in his RiscLua: "\" is a
shorthand for "function" and "=>" is a shorthand for "return". With
them we can write, for example,

  square = \(x) => x*x end

instead of:

  square = function (x) return x*x end

See:
  http://www.wra1th.plus.com/lua/notes/Scope.html
  (find-es "lua5" "risclua")

We will also use the notation [var := value] for substituion of a
single variable, and [var1 := value1, var2 := value2 ...] for
simultaneous substitution. A beta-reduction can be calculated in two
steps:

  (\(x) => x*x end)(5)  -~->  (x*x)[x:=5]
                        -^->   5*5



 «Class-short» (to ".Class-short")

4. "Class" with shorthands
==========================
Remember that "Class" and "otype" were defined as:

  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

With the shorthands for __mt and lambda we can rewrite these as:

  Class = { type   = "Class",
            __call = \(class, o) o.__mt = class; => o end,
          }
  Class.__mt = Class
  otype = \(o) local mt = o.__mt; => (mt and mt.type or type(o)) end




 «Vector» (to ".Vector")

5. A class "Vector"
===================
Consider this demo code:

--[=[

 (eepitch-lua51)
 (eepitch-kill)
 (eepitch-lua51)
dofile "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

--]=]

After running the line "w = ..." we will have this:

  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,
  }
  v = {
    3,
    4,
    __mt = Vector,
  }
  w = {
    20,
    30,
    __mt = Vector,
  }



 «Vector-box» (to ".Vector-box")

6. The "Vector" demo in box diagrams
====================================
...or, in box diagrams:

   <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--------+---------/
              :    ^
              \..../



 «Vector-reductions» (to ".Vector-reductions")

7. The "Vector" demo: reductions
================================
We can use reductions to understand "Vector {3, 4}" and "v:norm()".
Being slightly informal at some points, we have:

        Vector {3, 4}
  --~-> Vector({3, 4})
  --~-> Vector.__mt.__call(Vector, {3, 4})
  --~->       Class.__call(Vector, {3, 4})
  --~->             <fcal>(Vector, {3, 4})
  --~-> (\(class, o) o.__mt=class; => o) (Vector, {3, 4})
  --~->             (o.__mt=class; => o) [o:={3, 4}), class:=Vector]
  --~->                                      {3, 4, __mt=Vector}

        v = Vector {3, 4}
  --~-> v =        {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(V[1]^2+V[2]^2)) [V:={3, 4, __mt=Vector}]
  --~->          math.sqrt(   3^2+   4^2)
  --~->          math.sqrt(           25)
  --~->                                5
 


 «Class-boxes» (to ".Class-boxes")

6. Class boxes
==============
Here's another convention that becomes practical when we get used to
the notations above: the "Class box". A Class box for "Vector" depicts
Vector on top of Vector.__index, with a horizontal line separating
them, and the "__index" and "__class" entries of Vector become
implicit in the Class box. We get:

            /---+---\       /---+----\
        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:             utf-8-unix
-- End: