Classes And Methods |
|
Tables can be used as associative arrays. This functionality allows us to store functions in a class table, ie. with the function name mapped to the function.
A = {} function A:add(x,y) return x+y end print( A:add(1,2) ) -- prints 3
A
. If we wanted to make another instance we could make a table be, (eg. B={}
) and copy all of the methods from A
into B
.
Multiple instances of a class can be created by using Luas tag methods. The tag methods can be used to redirect function requests from a class instance to the class function table. ie. We no longer need to copy all of the classes functions into each instance of the class. eg.
-- settag() returns a table "{}" which has been tagged with a new tag value A = settag({},newtag()) -- use the index tag method to redirect a request for a function to the -- class function table settagmethod(tag(A),"index", function(t,f) return %A[f] end) function A:new(x,y) -- create an instance of class A local t = {x=x,y=y} settag(t,tag(A)) -- tag the new table to tell it what its class type is return t end function A:add() print (self.x+self.y) end function A:sum(a) assert(tag(self)==tag(a)) -- check they are same class print (self.x+a.x,self.y+a.y) end a = A:new(7,9) -- new instance of A a:add() b = A:new(2,4) -- new instance of A b:add() a:sum(b) -- "sum" of 2 instances
a:sub()
will cause a stack overflow as Lua looks infinitely for the unrecognised function and then the nil
that the tag method returns. This can be fixed by using rawget(table,key)
to fetch the function from the function table. rawget()
does not invoke any tag methods when getting the value of a key from a table.
settagmethod(tag(A),"index", function(t,f) return rawget(%A,f) end)
a.number = 123
a:number() -- try and call x
a.sum = 7 print( a.sum ) -- should this print <function> or 7 ? (it prints 7)
This problem cannot be resolved as the call request does not inform the tag method which type is required. We can take a preference, eg. class instance value takes preference over the function table value. (Doing it this we could support function overloading.)
If the function table takes preference, we could use the gettable tag method to check whether a method exists in the function table A
first, eg.
settagmethod(tag(A), "gettable", function(t,k) if rawget(%A,k) then return rawget(%A,k) else return rawget(t,k) end end )
Unified methods: This problem has been solved in Sol (a fork of Lua by Edgar Toernig) by introducing Unified methods. Here, a function/method table is associated with a tag. What happens when a table lookup occurs depends on how the lookup was invoked. ie. a.foo
and a:foo
have different implications. a.foo
looks for a member called "foo" and a:foo
looks in the tagged function table for a method "foo". Thus, you can have two members, one data and one a function happily living side by side, with no clash.
t.foo
/ t:foo
problem (and allow arbitrary data to be stored)?. It looks like the existing Lua tag method system, or modified? I don't think this is just the result of trying to emulate Python, there is a conflict here. ie. your implementation is restricted. I thought the metamechanisms were supposed to be flexible enough to implement features like this. Is this a question of implementation style? The problem could be avoided by using stand alone functions but I dont think thats very tidy (and in this example, unlike Python) --NDT
operator[]
or dedicated member functions. There is no ambiguity between what is a field access and what is not. Lua's tag methods are limited. You can't distinguish between table[x]
for accessing fields and table.x
/ table:x
for calling methods. The simple solution to your PythonDictionaries problem is to give up using native table syntaxes for element access and rely on methods such as get
and set
. It will make the implementation much simpler too! --JohnBelmonte