[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: debug.protoid(f) ? (Re: Type annotations for lua)
- From: nobody <nobody+lua-list@...>
- Date: Sat, 13 May 2023 04:07:46 +0200
On 2023-05-13 00:39, Pierre Chapuis wrote:
Anyway, far from me the idea to turn Lua into Python. I just thought
for once this proposal kind of fits the "mechanisms not policies" and
would be a good way to add type annotations to Lua *if* it gets some.
Apropos mechanisms: I've often wished for some general annotation /
metadata mechanism, basically "an extra slot in every value" plus some
API / syntax to access it. Could be used for types, but also doc
strings, profiling information, or whatever else you might need.
Generally, it's possible to invert this and instead use a big metadata
table indexed by values. (While special syntax might be nice-to-have, it
isn't actually all that important)… Except, unfortunately, closures are
a problem: In the case of
function newCounter( )
local i = 0
return function( ) i = i + 1 ; return i end
end
every instance of the inner function has a new identity, and you
currently can't associate some piece of metadata with *all* of them, at
least not without adding relatively a lot of code and a manual
association mechanism.
Having a `debug.protoid( f )` (returning a unique identifier [i.e. the
pointer] for the function's prototype, analogous to `debug.upvalueid( f,
n )` for the upvalue slots) would be a great tool, since then you can
actually associate the metadata with the function's prototype (that's
shared between all its instances / closures) without manually faking it.
With that, it's actually not too hard to (ab)use metamethods etc. to add
annotations to values that can then at the very least be used by e.g.
testing code, or (for docstrings) a fancy REPL.
* * *
Beyond that, a general annotation syntax that's allowed in weird places
(e.g. valid both as a statement and as part of an expression, and also
allowing e.g. `function () :: foo ... end` for `(function() ... end) ::
foo`, where `::` is the placeholder for whatever that thing ends up
being), that either defaults to doing nothing or just throwing an error,
and equipped with a metamethod for adding actual semantics, would allow
pretty comfortable usage… but this second part is pretty optional (and
way more complex than the first bit). With such a syntax, you can write
something like
function newCounter( ) :: fun(nil, fun(nil, number))
local i = 0 :: number
return function( ) :: fun(nil, number)
i = i + 1
return i
end
end
and if the metatable of whatever `fun` & `number` are has an appropriate
entry for `::` you get the type info associated / saved for the values,
such that later introspection can access & use it. Or if you want doc
strings…
function newCounter( ) :: [[
blah blub
]]
return function( ) :: "blop beep" i = i + 1 ; return i end
end
…looks quite readable. But following the definition with normal function
calls for annotations…
function newCounter( )
return function( ) i = i + 1 ; return i end
end
doc( newCounter ) [[
blah blub
]]
-- that's making an instance of the inner closure to then extract
its prototype to attach info…
doc( newCounter( ) ) [[
blop beep
]]
isn't actually that much worse, and *way* less complicated. (So fancy
syntax most likely wouldn't be worth it for me, but looking at whether
something like `debug.protoid` (or maybe an actual debug/doc/whatever
slot in every value[1]) could/should be added might be worth it?)
-- nobody
[1] Even if you have to flip a flag in luaconf.h for an actual slot to
exist, a "blessed" / unified interface that people can coordinate around
means collaboration is a lot easier than when everyone brews their own
annotation / metadata mechanism.