Getter

I’m trying to figure out getters in Codea. Testing the approache from stackoverflow http://stackoverflow.com/questions/10578935/lua-getters-and-setters but it doesn’t work.

I have a test code like this

Foo = class()

function Foo:greeting()
    return "My name is"
end

function setup()
    local obj = Foo()
    print(obj.greeting(), obj.name)
end

It works just fine giving an output like

My name is nil

But if I add before setup() a function

function Foo:__index(key)
    if key == "name" then
        return "foo"
    else
        return rawget(self, key)
    end
end

I’m getting an error like “attempt to call field ‘greeting’ (a nil value)”

So I got lost. How should I implement getters in Codea?

I’m not sure what your trying to do, but here’s an example.

http://nova-fusion.com/2011/04/04/implementing-proper-gettersetters-in-lua/

Thank you. Of course I saw this article as it’s the first in googling this subject. But it doesn’t work for me either. I get nil error or stack overflow error (when using “self.class.__classDict” instead of “Test.__classDict”).

And my point is to write a simple getter (and setter) for a property - nothing more. If you’ll run my code you’ll see the problem. And I got really stuck with it.

Does anybody use getters in Codea? Could you share some example code please?

@Iavmax I haven’t the slightest idea of what your trying to do, but I took your code and was playing with it. If I take the () off the line print( obj.greeting,obj.name) I don’t get an error, but I’m not sure what result I’m supposed to get. Maybe this will help some.


Foo = class()

function Foo:greeting()
    return "My name is"
end

function setup()
    local obj = Foo()
    print(obj.greeting, obj.name)
end

function Foo:__index(key)
    if key == "name" then
        return "foo"
    else
        return rawget(self, key)
    end
end

@Iavmax Like I said above, I don’t know what you’re trying to do but here’s what I found out playing with the code. Apparently by using __index, you’re reading a table. That table is the self values that get created in Foo: init(). It doesn’t look like anything other than __index gets called using obj. If I run this code, I think it prints what you’re after. Also, when you hit the return rawget code in the function Foo:__index(key), it calls Foo__index again and goes into an infinite loop and causes an error.


function setup()
    local obj = Foo()
    print(obj.greeting, obj.name)
end

Foo = class()

function Foo:init()
    self.greeting="My name is"
end

function Foo:__index(key)
    if key == "name" then
        return "foo"
    else
        return rawget(self.key)
    end
end

@Iavmax I also found this. Again, not sure if this is what you’re after but it seems to allow you to set values and read them back.


function setup()
    foo = MyClass()

    foo.testMember = 5
    foo.testMember = 2

    print( foo.testMember )
end

MyClass = class()

function MyClass:init()
    self.members = {}
end

function MyClass:__newindex( index, value )
    if index == "testMember" then
        self.members[index] = value
        print( "Set member " .. index .. " to " .. value )
    else
        rawset( self, index, value )
    end
end

function MyClass:__index( index )
    if index == "testMember" then
        print( "Getting " .. index )
        return self.members[index]
    else
        return rawget( self, index )
    end
end

@Iavmax I just realized the code I have above is the same code in your link from your first post. The code I show above seems to work if I understand getters and setters correctly. So I’m not sure why you say in your first post that it doesn’t work.

@lavmax Why do you even want to make getters and setters? They’re usually only used in OOP, when you have a private variable that other classes can’t reach without a getter/setter. In Lua, there are no private variables, and you could access any variable from any class. The only place I could see you needing this is if you have a variable that is local to a tab, but then, couldn’t you just simply have 2 functions, one setting the variable, and the other returning it?

@dave1707 The problem is that your code (and mine) is not working. Just add any function to your class and you’ll get an error. Like this


function setup()
    foo = MyClass()

    foo.testMember = 5
    foo.testMember = 2

    print( foo.testMember )
    
    foo:foo()
end

MyClass = class()

function MyClass:init()
    self.members = {}
end

function MyClass:foo()
    print("functions work too")
end

function MyClass:__newindex( index, value )
    if index == "testMember" then
        self.members[index] = value
        print( "Set member " .. index .. " to " .. value )
    else
        rawset( self, index, value )
    end
end

function MyClass:__index( index )
    if index == "testMember" then
        print( "Getting " .. index )
        return self.members[index]
    else
        return rawget( self, index )
    end
end

And who needs a class without functions. Theoretically there should be a property __classDict that holds class functions, but in Codea it is nil. So may be @Simeon could bring some light into the subject.

@SkyTheCoder I need it for dynamic properties, that are calculated, not stored. Of course I can use c++ style with getter/setter functions, but it’s very ugly and uncommon in languages like lua.

@Iavmax I understand what you’re saying now.

@Iavmax When you create a class, you create the metatable of the following instances. In your example, the method foo is linked to the metatable of the instance foo (foo = MyClass). In the __index getter, self refer to the instance, not it’s metatable. A silly fix to illustrate:

function MyClass:__index( index )
   if index == "testMember" then
      print( "Getting " .. index )
      return self.members[index]
   else
      if index == "foo" then
         return rawget(getmetatable(self), index)
      end
      return rawget( self, index )
   end
   --[[ or handle all case with
   else
         return rawget(getmetatable(self), index) or rawget( self, index )
   end]]
end

@Iavmax Here’s a way that I came up with. It does the __index and __newindex methods and also any class function. I added some examples of using the index and newindex methods. Not sure if this is what you’re after, but I’m learning about metatables.


function setup()
    z=xx()  -- create instance of xx
    
    z:update(10,12345)  -- update key 10 with value 12345
    z:read(10)  -- read key 10
    
    z:read(2)   -- read key 2, error, key doesnt exist
    
    z:update(24,10) -- update key 24 with value 10, value out of range for key
    
    z:test()    -- call function test
end

xx=class()

function xx:init()
    self.tab1={}    -- table for values
    self.tab2={}    -- table for meta methods
    setmetatable(self.tab1,self.tab2)  

    -- create meta function for table read
    self.tab2.__index=  
        function(tab,key)
            local r=rawget(tab,key) -- read table
            if r==nil then  -- entry not found
                return "key not found"
            else
                return r
            end
        end

    -- create meta function for table update
    self.tab2.__newindex=   
        function(tab,key,val)
            if key==24 and (val<20 or val>30) then  -- set limit for key
                print("key "..key,"value "..val.." out of range, not updated")
            else
                rawset(tab,key,val) -- update table
            end
        end 
end

function xx:update(k,v)
    print("update key "..k.." with value "..v)
    self.tab1[k]=v
end

function xx:read(k)
    print("read key "..k.." ",self.tab1[k])
end

function xx:test()
    print("the test function was called")
end

@toffer Thank you! I think I’m starting to get it. My next question could seem strange but why do we need

return = rawget(self, index)

if __index itself is called only when there is no such key in an object?

@dave1707 Thank you for your example.

@lavmax you’re right, I’ve blended the way I usualy implement getters with your example (a single case property lookup). So to respect the property lookup chain, something like this should be better

function MyClass:__index( index )
    return self.members[index] or getmetatable(self)[index]
end

@toffer Got it, finally. Thank you so much!