Kapulani
Kapulani

Reputation: 95

Can I override a Lua table's return value for itself?

Is it possible for a table, when referenced without a key, to return a particular value rather than a reference to itself?

Let's say I have the following table:

local person = {
    name  = "Kapulani",
    level = 100,
    age   = 30,
}

In Lua, I can quite easily refer to "person.name", "person.level", or "person.age" and get the values as expected. However, I have certain cases where I may want to just reference "person" and, instead of getting "table: " I'd like to return the value of "person.name" instead.

In other words, I'd like person.x (or person[x]) to return the appropriate entry from the table, but person without a key to return the value of person.name (or person["name"]). Is there a mechanism for this that I haven't been able to find?

I have had no success with metatables, since __index will only apply to cases where the key does not exist. If I put "person" into a separate table, I can come up with:

local true_person = {
    ... -- as above
}

local env_mt = {
    __index = function(t, k)
        if k == 'person' then
            return true_person
        end
    end
}

local env = setmetatable( {}, env_mt )

This lets me use __index to do some special handling, except there's no discernable way for me to tell, from __index(), whether I'm getting a request for env.person (where I'd want to return true_person.name) or env.person[key] (where I'd want to return true_person as a table, so that 'key' can be accessed appropriately).

Any thoughts? I can approach this differently, but hoping I can approach this along these lines.

Upvotes: 3

Views: 2697

Answers (2)

hugomg
hugomg

Reputation: 69934

I am not sure that what you are asking for is a good idea because it flies in the face of compositionality. Usually one would expect the following two programs to do the same thing but you want them to behave differently

print(person.name)

local p = person
print( p.name )

Its also not very clear how assignment would work. person.age = 10 should change the age but person = otherPerson should change the reference to the perrson, not the age.

If you don't care about compositionality and are onyl reading data, then a more direct way to solve the problem is to have a query function that receives the fields encoded in a string

query("person.age")   -- 17
query("person.name")  -- "hugomg"
query("person")       -- 17; query gets to default to whatever it wants.

To keep the syntax more lightweight you can omit the optional parenthesis

q"person.age"
q"person"

Or you can extend the __index metamethod on the global table, _G

setmetattable(_G, { __index = function(self, key) return query(key) end })

print ( person_age )  -- You will need to use "_" instead of "." for the
                      -- query to be a valid identifier.

Upvotes: 1

Etan Reisner
Etan Reisner

Reputation: 80921

You can do it when the table is being used as a string by setting the __tostring metatable entry:

$ cat st.lua
local person = {
    name  = "Kapulani",
    level = 100,
    age   = 30,
}

print(person)
print(person.name)
print(person.age)

setmetatable(person, {__tostring = function(t) return t.name end})
print(person)

$ lua st.lua
lua st.lua
table: 0x1e8478e0
Kapulani
30
Kapulani

Upvotes: 2

Related Questions