[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Observations about a index getters
- From: Andrew Starks <andrew.starks@...>
- Date: Fri, 5 Dec 2014 22:20:20 -0600
I have an object model that I was hacking up. I decided to implement a
penlight-like 'sorta private' getter/setter model. Where setting and
getting can be independently controlled by prefixing fields with a
single `_` and methods are defined by prefixing fields with `get_` or
`set_`.
Unlike penlight (which is excellent), mine returns `_*` fields. In
doing so it makes fields remain public, which means that to make them
private/immutable/calculated, you need to make the getter/setter
return `nil` or `error` or whatever. This is what *I* usually want
(example: give them the plain value if they ask, but error if they try
to set it), so I did it this way to avoid writing lots of functions
that simply return the value.
My first attempt (which does not work and can you spot why?):
```
function class:__index(index)
if type(index) == 'string' then
return rawget(self, '_' .. index)
or class['get_' .. index] and class['get_' .. index](self)
or class[index]
end
return class[index]
end
```
If you're new to Lua, and is not already clear, this should help you
spot the bug:
```
function class:__index(index)
local value = class[index]
if value ~= nil then
return value
end
value = rawget(self, '_' .. index)
if value ~= nil then
return value
end
return class['get_' .. index] and class['get_' .. index](self) or nil
end
```
There are less tedious ways to write this, but those involve calling
`rawget` twice on
match; once for `~= nil` and again for the value.
As a side interest, I would be happy to read alternate implementation
ideas for the above. I know that there are small optimizations that
could be done, etc. I don't like the general style of the above
solution, which led to some exploring.
I stumbled upon this model, which I had not observed before, although
it no doubt has been done before (if I am not missing some fatal
flaw):
```
class.index_methods = {
function(self, index)
return class[index]
end,
function(self, index)
return rawget(self, '_' .. index)
end,
function(self, index)
return class['get_' .. index] and class['get_' .. index](self)
end
}
function class:__index(index)
for i, method in pairs(class.index_methods) do
local value = method(self, index)
if value ~= nil then
return value
end
end
return nil
end
```
Although for this example, this implementation wasteful and over done,
I like this model for more complex cases. To me, it seems clean and
extensible. I have not benchmarked it, but I will assume that it is
also relatively slow, when compared to simpler designs, especially.
However, if multiple fallbacks are a requirement, then this might be
as (in)efficient as any other way. I like it over my other attempts,
so far.
--Andrew
*No proposals were harmed (or contemplated) during the making of this post. :)
**Also, the above code was not tested, after modifying it for clarity.