• Subject: Alternative tuple implementations
• From: RLake@...
• Date: Mon, 30 Jun 2003 10:38:50 -0500

```Wim escribió:

> On the other hand, I think that Python tuples can be emulated by Lua
tables
(arrays) if you use a proxy.  Simple approach:

<snip>

Here's an alternative which doesn't make as_table freely available:

function tuple(...)
return setmetatable({}, {
__index = function(_, index) return arg[index] end,
__newindex = function() error "tuples are immutable!" end
})
end

Unfortunately, this creates three tables (one empty) and a closure
for each tuple, while Wim's original only creates two tables for
each closure.

For efficiency, we can use a standard weak-table registry solution:

do
local tuples = setmetatable({}, {__mode = "k"})

local meta = {
__index = function(self, index) return tuples[self][index] end,
__newindex = function() error "tuples are immutable!" end
}

function tuple(...)
local self = setmetatable({}, meta)
tuples[self] = arg
return self
end
end

Now, the only problem we have is that if tuple A is a member of itself
(possibly through a chain of other tuples or objects), then the garbage
collector will never clean the tuple out of the weak table. This certainly
cannot happen if the tuples are always simple objects, like strings or
numbers.

Neither of these solutions solves the tuple-equality problem I mentioned
earlier, though.

For a specific tuple implementation, like a vector, you can also use a
functional approach (this is more or less adapted from SICP, I think)
which also leaves the tuple-equality problem hanging:

function vector(_x, _y, _z)
return function(fn) return fn(_x, _y, _z) end
end

function x(_x, _y, _z) return _x end
function y(_x, _y, _z) return _y end
function z(_x, _y, _z) return _z end
function set_x(_x)
return function(_, _y, _z) return vector(_x, _y, _z) end
end
function set_y(_y)
return function(_x, _, _z) return vector(_x, _y, _z) end
end
function set_z(_z)
return function(_x, _y, _) return vector(_x, _y, _z) end
end

> p1 = vector(1, 2, 3)
> =p1(x)
1
> =p1(z)
3
> =p1(print) -- :)
1       2       3
>
> =p1(set_x(42))(print)
42      2       3

That this has considerable expressive power can be seen from:

function vlength(_x, _y, _z) return math.sqrt(_x * _x + _y * _y + _z * _z)
end

return function(_x, _y, _z)
return vector(_x + v2(x), _y + v2(y), _z + v2(z))
end
end

-- p1 was not changed by the set_x above
> =p1(print)
1       2       3

> =p1(vlength)
3.7416573867739

2       4       6

Obviously, the accessor and mutator functions above could be easily
generated automatically from a list of member names. The syntax could be
improved by using metamethods; this could also be used to put accessors
and mutators in an appropriate namespace. Unfortunately, function closures
cannot have metamethods, but you can achieve a very similar effect with a
table with a __call metamethod.

(This solution does put a bit of a load on the memory management system.
:) )

R.

R.

```