[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: [Feature Request?] __key
- From: Sean Conner <sean@...>
- Date: Sun, 17 Jul 2016 04:05:52 -0400
It was thus said that the Great Tim Hill once stated:
>
> >
> > There are exactly two times when the __key metamethod gets called:
> >
> > t[ud] = v --> t[ud.__key()] = v
> > v = t[ud] --> v = t[ud.__key()]
> >
> > Of note, it does NOT get called in this situation:
> >
> > k = ud.__key()
> > assert(t[k] == v) -- no __key used here!
> >
> > But this absolutely ought to work.
> >
> > Yes, there's ONE extra check made when accessing a table ("does the
> > key have a __key metamethod, yes or no?") so yes it does TECHNICALLY
> > make things more expensive, but this isn't anywhere close to a "good
> > luck with performance" kind of impact.
> >
>
> Where is the synthetic key value stored then? It must be stored in the
> key+value slot of the table. Where is the userdata stored? In the same
> place. So now we are storing three items per table slot, not two. Seems to
> me a significant impact.
To clarify Tim's position here:
foo = require "foo" -- getting away from the bigint thingy
t = {}
x = foo.make('alpha')
t[x] = "booya!" -- __key method triggered on 'x'
x = nil
collectgarbage('collect')
collectgarbage('collect')
collectgarbage('collect') -- just to make sure!
y = foo.make('alpha') -- intended to return the same __key as 'x'
print(t[y]) -- __key method triggered on 'y'
Other things to keep in mind ...
t = {}
x = foo.make('alpha')
t[x] = "booya!"
x = nil
collectgarbage('collect') -- repeat as needed
for name,value in pairs(t) do
print(name) -- Um ... what now?
print(type(name)) -- and what is the type?
print(getmetatable(name)) -- and does it have a metatable?
print(debug.getuservalue(name)) -- or a uservalue?
end
alpha = getmetatable(foo.make('alpha'):__key()
beta = getmetatable(foo.make('beta'):__key()
assert(alpha ~= beta)
assert(beta ~= alpha)
collectgarbate('collect') -- repeat as needed
ALPHA = getmetatable(foo.make('alpha'):__key()
BETA = getmetatable(foo.make('beta'):__key()
assert(ALPHA == alpha)
assert(alpha == ALPHA)
assert(BETA == beta)
assert(beta == BETA)
collectgarbage('collect') -- again, repeat as needed
assert(ALPHA == alpha)
assert(BETA == beta)
-- add other combinations as required
t = {}
t[true] = "alpha"
t[false] = "beta"
t[1] = "gamma" -- or any integer key in the table
t[1.5] = "delta" -- " number "
t['alpha'] = "epsilon" -- " string "
t[t] = "zeta" -- " table "
t[print] = "eta" -- " function "
t[coroutine.running()] = "theta" -- " coroutine "
t[io.stdin] = "iota" -- " userdata " [1]
-- ----------------------------------------------------------------
-- In English, the symbol " can be used for 'ditto', which loosely
-- means "the same", so for the above comments, each one should be
-- read as "or any ______ key in the table".
--
-- [1] The table given is not intended at this point to have any key
-- as a result of foo.make() in it, so the following, if I
-- understand this correctly, should not overwrite any existing
-- field of the given table (I'm going for a generalized __key thing
-- here, not just a "bigint" as per the previous example).
-- ----------------------------------------------------------------
x = foo.make('alpha')
t[x] = "booya!"
> And yes, a given __key *should* return the same value each time it is
> called for a given userdata in a given state. But if it doesn’t strange
> things happen. Strange in that they vastly complicate the table contract.
> And to what end?
>
> I’m not being obtuse here, I just don’t see the bang for the buck.
As I present these examples, I'm just not seeing it either, given that a
module that wants the behavior of '__key' can just return interned values.
-spc (__key is hard! Let's make rockets!)