Discussion:
__call?
Ando Sonenblick
2003-12-12 13:49:41 UTC
Permalink
Gang,

Thanks to all for help with my lua question about auto initing nil
values in a table to zero...

A couple replies utilize the __call metamethod. I'm not terribly
familiar with this one and the manual only has this to say about it:

• "call": called when Lua calls a value.
function function_event (func, ...)
if type(func) == "function" then
return func(unpack(arg)) -- primitive call
else
local h = metatable(func).__call
if h then
return h(func, unpack(arg))
else
error("...")
end
end
end


Can anyone give me more info on this? What exactly does it mean when
"Lua calls a value"?

Can I use this to distinguish between obj:Method() and obj.method?

Thanks for any insight...

Ando
-----------------------
Ando Sonenblick
SpriTec Software
www.spritec.com
Spencer Schumann
2003-12-12 14:29:09 UTC
Permalink
The __call metamethod allows a table to be used as a function. This is similar to C++'s operator
() for overloading the function call operator on objects.

Example using __call:

local mt =
{
__call = function(t, quote)
print(t.name .. " says, \"" .. quote .. "\"")
end
}

function makeperson(name)
local t = {name = name}
setmetatable(t, mt)
return t
end

local bart = makeperson("Bart Simpson")
bart("I didn't do it!")


The __call metamethod is passed the table used as a first argument, which allows the metamethod to
access the table that was used in performing the function call.

Of course, for this example, closures can be used for the same purpose.


Example using Closures:

function makeperson2(name)
return function(quote)
print(name .. " says, \"" .. quote .. "\"")
end
end

local ralph = makeperson2("Ralph Wiggam")
ralph("Daddy says I'm this close to sleeping in the yard.")


The method of using __call would allow for other metamethods to also be used on a given object,
hich can make it more flexible than just using closures.

As far as distinguishing between obj:Method() and obj.Method(), are you trying to determine
whether the method was called improperly without using the method call syntax? If so, it seems
that you just need to check that the first argument is a valid table.
Post by Ando Sonenblick
Gang,
Thanks to all for help with my lua question about auto initing nil
values in a table to zero...
A couple replies utilize the __call metamethod. I'm not terribly
� "call": called when Lua calls a value.
function function_event (func, ...)
if type(func) == "function" then
return func(unpack(arg)) -- primitive call
else
local h = metatable(func).__call
if h then
return h(func, unpack(arg))
else
error("...")
end
end
end
Can anyone give me more info on this? What exactly does it mean when
"Lua calls a value"?
Can I use this to distinguish between obj:Method() and obj.method?
Thanks for any insight...
Ando
-----------------------
Ando Sonenblick
SpriTec Software
www.spritec.com
__________________________________
Do you Yahoo!?
New Yahoo! Photos - easier uploading and sharing.
http://photos.yahoo.com/
Gustavo Niemeyer
2003-12-16 15:46:26 UTC
Permalink
Post by Spencer Schumann
The __call metamethod allows a table to be used as a function. This
is similar to C++'s operator () for overloading the function call
operator on objects.
[...]

As an interesting point, it should be noticed that callable objects
unfortunately are not real functions, and any code that tests for a
function type fails to detect the callable object. For example,
table.foreach() can't receive a callable object as the second
argument.

I've noticed problems like this while working on Lunatic Python. I ended
up implementing the __call method, but also wrapping Python functions
and methods in real Lua function closures, overwhelming these problems.
--
Gustavo Niemeyer
http://niemeyer.net
Virgil Smith
2003-12-16 17:25:06 UTC
Permalink
Post by Gustavo Niemeyer
As an interesting point, it should be noticed
that callable objects unfortunately are not
real functions, and any code that tests for a
function type fails to detect the callable
object. For example, table.foreach() can't
receive a callable object as the second
argument.
My $0.02 -- followed by a couple questions...

So, this is a bug-report for lua_isfunction more or less, since callable
objects <should> be compatible with functions. This would fully enable C++
"functor" style capabilities (i.e. state tracking).

Unfortunately, whether changing lua_isfunction is a "good" thing is not 100%
straight forward. At least one reason for this is that (AFAIK) Lua types
are static, which is to say that a value cannot have it's type changed after
creation (of course a variable can have a value of a different type assigned
to it, but this is a different statement). However, the classification of
an object as a "callable object" (i.e. of some type compatible, for the
purposes of this discussion, with functions), depends on an entry in the
object's metatable (which can change). The problem with that dynamism is
that a code construct that saves a "copy" of such a value (after checking it
for calling capability) for later use, may throw an error during that later
use if the object's metatable is modified between the time of the safety
check and the actual use (i.e. calling) of the object.

Naturally, lua_isstring, lua_isnumber, and lua_istable suffer from similar
issues. The easiest of these to understand is lua_isstring, because of the
behavior of tostring checking for a _tostring [sp] member of the object's
metatable. While AFAIK no such metatable member exists for numbers or
tables they are conceptually similar and such a metatable member could be
put forth as a standard.

A related issue is the question of the distinction between userdatas and
tables. Several people have suggested the use of a userdata acting as a
proxy for a table in order to work around the (in my opinion) bug/flaw in
Lua that _gc metamethods are not called for tables. While this does work,
it, unfortunately, breaks the objects classification as a table (lua_istable
will return false). Furthermore, next will not function on such an object.
Personally, I think that the deficiency with next and proxy tables should be
resolved by the addition of a standard metamethod for "next behavior", but
others may have better ideas.

----
So, does anyone have suggestions for type "emulation"/compatibility
flexibility in Lua?

To the Lua authors, are there any plans for changes in 5.1 or later to
address these issues?
Gustavo Niemeyer
2003-12-17 16:33:13 UTC
Permalink
Post by Virgil Smith
Unfortunately, whether changing lua_isfunction is a "good" thing is
not 100% straight forward. At least one reason for this is that
(AFAIK) Lua types are static, which is to say that a value cannot have
IMO, changing lua_isfunction is a bad thing. The right way to fix that
would be to introduce "lua_iscallable", or "lua_tofunction", or
something similar.
Post by Virgil Smith
it's type changed after creation (of course a variable can have a
value of a different type assigned to it, but this is a different
statement). However, the classification of an object as a "callable
object" (i.e. of some type compatible, for the purposes of this
discussion, with functions), depends on an entry in the object's
metatable (which can change). The problem with that dynamism is that
a code construct that saves a "copy" of such a value (after checking
it for calling capability) for later use, may throw an error during
that later use if the object's metatable is modified between the time
of the safety check and the actual use (i.e. calling) of the object.
It looks to me like a "doctor it hurts" case. If the code is supposed
to detect callability changes, just test it multiple times. If the
code is supposed to store a value which should always be callable, don't
turn it in something not callable or it will throw an error.
Post by Virgil Smith
Naturally, lua_isstring, lua_isnumber, and lua_istable suffer from similar
issues. The easiest of these to understand is lua_isstring, because of the
behavior of tostring checking for a _tostring [sp] member of the object's
[...]

Notice that lua_tostring doesn't check for the __tostring member. It's
the "tostring()" Lua function that checks. Have a look at the luaB_print
and luaB_tostring implementations.
--
Gustavo Niemeyer
http://niemeyer.net
Stoil Todorov
2003-12-17 17:12:24 UTC
Permalink
Hi ,
I have the problem with load/call Lua code:-(
I follow the next order:
1. luaL_loadbuffer(...)
2. lua_getglobal(m_pLuaState, "read");
3. lua_pcall( m_pLuaState, 0, 0, 0 );
but I receive "A runtime error.: attempt to call a nil
value" - t.e. function "read" is not in Global state,
but I see it is in string, which I loaded...:-(
I load string with this code:
luaL_loadbuffer(m_pLuaState, m_strCode,
m_strCode.GetLength(), (LPCTSTR)m_strCode);

Sorry about this "beginner" question and thank you in
advance...
Stoil



__________________________________
Do you Yahoo!?
New Yahoo! Photos - easier uploading and sharing.
http://photos.yahoo.com/
Virgil Smith
2003-12-17 17:35:38 UTC
Permalink
The key here lies in better understanding what luaL_loadbuffer does.
It "loads" the code, but does not execute it (which would give it the
opportunity to set globals such as "read").
Essentially luaL_loadbuffer returns on the stack a function whose body
consists of the loaded code, you can then call this function to run it (or
store it off in some table, or whatever).

Checking the manual/sources/and the list archives for lua_dobuffer should
help you better understand this (provided it doesn't swamp you with
results). Looking directly at the source for lua_dobuffer should help.

-----Original Message-----
From: lua-***@bazar2.conectiva.com.br
[mailto:lua-***@bazar2.conectiva.com.br]On Behalf Of Stoil Todorov
Sent: Wednesday, December 17, 2003 11:12 AM
To: Lua list
Subject:


Hi ,
I have the problem with load/call Lua code:-(
I follow the next order:
1. luaL_loadbuffer(...)
2. lua_getglobal(m_pLuaState, "read");
3. lua_pcall( m_pLuaState, 0, 0, 0 );
but I receive "A runtime error.: attempt to call a nil
value" - t.e. function "read" is not in Global state,
but I see it is in string, which I loaded...:-(
I load string with this code:
luaL_loadbuffer(m_pLuaState, m_strCode,
m_strCode.GetLength(), (LPCTSTR)m_strCode);

Sorry about this "beginner" question and thank you in
advance...
Stoil



__________________________________
Do you Yahoo!?
New Yahoo! Photos - easier uploading and sharing.
http://photos.yahoo.com/
Virgil Smith
2003-12-17 17:25:50 UTC
Permalink
Post by Gustavo Niemeyer
IMO, changing lua_isfunction is a bad thing. The right way to
fix that would be to introduce "lua_iscallable", or
"lua_tofunction", or something similar.
Off hand, I'd agree with you, but the question is one of consistency. Since
lua_isstring makes a "compatibility" statement rather than a literal "the
type of this is string" statement then lua_isfunction should behave
similarly. Of course a "function compatibility" statement is a stronger
statement than "iscallable", because you can do more with a function than
just call it IIRC (manipulation of upvalues anyone?).
Post by Gustavo Niemeyer
It looks to me like a "doctor it hurts" case. If the code
is supposed to detect callability changes, just test it
multiple times. If the code is supposed to store a value
which should always be callable, don't turn it in
something not callable or it will throw an error.
Yes, in practice the solution is obvious, however, from a language
design/elegance/clarity standpoint I see it as an issue. Specifically, what
I see as an issue is that user defined types are not static in Lua. Of
course I think this is more a matter of how <I> think, and not a question of
a problem with Lua.
Another way of looking at this is to contrast Class Oriented Programming
with true Object Oriented Programming (think of contrasting C++ with say,
Smalltalk). Lua is clearly Object Oriented, not Class Oriented, however, by
sharing metatables, Lua allows Class Oriented styles of operations.

So, um, yeah your right. I just need to accept (metaphorically speaking) not
only that its possible for a specific "horse" to change into a dog, but that
at any point the definition of what a "horse" is, may change. :P
Post by Gustavo Niemeyer
Notice that lua_tostring doesn't check for the __tostring
member. It's the "tostring()" Lua function that checks.
Have a look at the luaB_print and luaB_tostring
implementations.
Um, yep. Thanks for the correction.

--
Gustavo Niemeyer
http://niemeyer.net
Edgar Toernig
2003-12-17 18:40:39 UTC
Permalink
Post by Gustavo Niemeyer
Post by Virgil Smith
Unfortunately, whether changing lua_isfunction is a "good" thing is
not 100% straight forward. At least one reason for this is that
(AFAIK) Lua types are static, which is to say that a value cannot have
IMO, changing lua_isfunction is a bad thing. The right way to fix that
would be to introduce "lua_iscallable", or "lua_tofunction", or
something similar.
Deja vu... See

http://lua-users.org/lists/lua-l/2001-11/msg00399.html

and the Follow-Ups...

Ciao, ET.
Roberto Ierusalimschy
2003-12-17 17:00:48 UTC
Permalink
Post by Virgil Smith
To the Lua authors, are there any plans for changes in 5.1 or later to
address these issues?
Currently we have no plans to change that. As pointed out,
lua_isfunction is different from lua_iscallable. If you really want
that, it is quite easy to define a reasonable lua_iscallable using the
current API.

About "next" for non-tables: maybe "pairs" could look for a __next
metamethod to do the iteration; that is reasonable, but we have not
discussed that yet.

-- Roberto
Gustavo Niemeyer
2003-12-19 20:39:16 UTC
Permalink
Post by Roberto Ierusalimschy
Post by Virgil Smith
To the Lua authors, are there any plans for changes in 5.1 or later
to address these issues?
Currently we have no plans to change that. As pointed out,
lua_isfunction is different from lua_iscallable. If you really want
that, it is quite easy to define a reasonable lua_iscallable using the
current API.
[...]

It might be a good idea to at least fix the standard library in this
respect, either completely removing the type checking from table.foreach
and others, or implementing a generic checking mechanism.
--
Gustavo Niemeyer
http://niemeyer.net
Gustavo Niemeyer
2003-12-19 20:36:55 UTC
Permalink
Post by Gustavo Niemeyer
Post by Spencer Schumann
The __call metamethod allows a table to be used as a function. This
is similar to C++'s operator () for overloading the function call
operator on objects.
[...]
As an interesting point, it should be noticed that callable objects
unfortunately are not real functions, and any code that tests for a
function type fails to detect the callable object. For example,
table.foreach() can't receive a callable object as the second
argument.
I've noticed problems like this while working on Lunatic Python. I ended
up implementing the __call method, but also wrapping Python functions
and methods in real Lua function closures, overwhelming these problems.
I've written a few words about this, and published a generic scheme,
based on the solution used at Lunatic Python for workarounding this
problem, at my blog:

https://moin.conectiva.com.br/GustavoNiemeyer
--
Gustavo Niemeyer
http://niemeyer.net
Bilyk, Alex
2003-12-19 20:57:53 UTC
Permalink
local function callable_to_function(callable)
return
function(...)
return callable(unpack(arg))
end
end

local callable_table = setmetatable({}, {__call = function (t, k, v) print(k,v) end})

foreach({a = 1, [3] = 'hello'}, callable_to_function(callable_table))
Post by Virgil Smith
-----Original Message-----
Sent: Friday, December 19, 2003 12:39 PM
To: Lua list
Subject: Re: __call?
Post by Roberto Ierusalimschy
Post by Virgil Smith
To the Lua authors, are there any plans for changes in 5.1 or later
to address these issues?
Currently we have no plans to change that. As pointed out,
lua_isfunction is different from lua_iscallable. If you really want
that, it is quite easy to define a reasonable lua_iscallable
using the
Post by Roberto Ierusalimschy
current API.
[...]
It might be a good idea to at least fix the standard library in this
respect, either completely removing the type checking from
table.foreach
and others, or implementing a generic checking mechanism.
--
Gustavo Niemeyer
http://niemeyer.net
Gustavo Niemeyer
2003-12-19 23:14:04 UTC
Permalink
Post by Bilyk, Alex
Post by Gustavo Niemeyer
It might be a good idea to at least fix the standard library in this
respect, either completely removing the type checking from
table.foreach
and others, or implementing a generic checking mechanism.
[...]
Post by Bilyk, Alex
local function callable_to_function(callable)
[...]

Cool! I never thought about doing that in pure Lua. I've added
the example in my blog, with the usual credits.

OTOH, notice that neither your solution nor mine change the fact
that it'd be nice if the standard library accepted callables
without this hack.
--
Gustavo Niemeyer
http://niemeyer.net
R***@oxfam.org.pe
2003-12-19 21:33:03 UTC
Permalink
As Edgar pointed out a couple of days ago (and a couple of years ago),
there is no obvious reason why table.foreach should check to see if its
argument is a function. If it is not callable, a perfectly understandable
error message would be generated when table.foreach tries to call it.

The situation is a little different for some other places where functions
are used polymorphically, such as in the __index/__newindex metamethods
(and some other metamethods), the "legacy" for-in construct, string.gsub()
and probably some others I have forgotten about.

A slightly similar issue occurs with "table-like" userdata (i.e. userdata
with __index and maybe __newindex metamethods). These should "act like"
tables, and they usually do, but not always: for example, the environment
must be a "real" table, and again there is the issue with
__index/__newindex metamethods. (Also, the metatable must be a "real"
table.)

It is not sufficient to say that a table-like object has an __index
metamethod and a function-like object has a __call metamethod, because
objects can have both (tables with __call metamethods, for example). So
attempting to use the tableness or functionness of an object to select the
appropriate behaviour is doomed to ambiguity if both tableness and
functionness are possibilities. (This is not the case with, for example,
string.gsub, although it would be perfectly reasonable for string.gsub to
use the first capture as a table key if the replace argument were
table(like).)

So there is a certain lack of orthogonality. But that is not a disaster.

I would suggest that, to the extent possible, library functions not check
for function/table-ness, and rather leave it to lua_{get, set, call,
pcall} to generate the error message -- it may not be as precise about
identifying which argument is in error, but it is otherwise an adequate
error message. (And avoiding the use of lua_raw{get, set} would probably
be a good idea.) Some solution like Alex's is possible for places where
either a table or a function is acceptable with different behaviours, and
possibly also for the case of environment tables. Allowing userdata as
metatables is arguably not worth the overhead it would generate, although
there are applications for it.

I know that some of us have thought (out loud, either on this list or in
private correspondence) about whether it would be possible to simply merge
the concept of functions and the concept of tables. Unfortunately, there
are a number of subtle semantic issues and charming though the idea is, it
is probably not worthwhile either.

Rici
Loading...