lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]




On Wed, Nov 19, 2014 at 7:25 PM, Rena <hyperhacker@gmail.com> wrote:
On Wed, Nov 19, 2014 at 4:26 PM, Thiago L. <fakedme@gmail.com> wrote:
> <SoniEx2> %5.3 t=setmetatable({")", "'test'", "print("},{__call =
> function(t,...) return table.remove(t) end}) load(t)()
> <yalb> SoniEx2: sandbox:1: bad argument #1 to 'load' (function expected, got
> table); stack traceback:; [C]: in function 'load'; sandbox:1: in main chunk
>
> Can we please get a callable API? (for example, iscallable(obj), or in C,
> lua_iscallable(whatever))

This is something I've brought up before (and seems to fit in with the
current int vs float discussion as well). In a lot of cases it's easy
and convenient to use Duck Typing in Lua. You don't check types, you
check capabilities (can this be called? does it have an "animate"
method?) or you just assume the objects are compatible and let an
error be thrown if not.

Lua already does this with metatables. It doesn't matter when you
write "x + y" if x and y are numbers, strings, tables, userdata; it
just tries to add them (using built-in logic or __add metamethod) and
if it can't, it complains.

Unfortunately it's not 100% possible to write code that never checks
or requires a specific type, even when it doesn't need to:

-Maybe you want to accept either a string or a function which
(hopefully) returns a string. Without a way to ask "is this object
callable?" you can only either use pcall (adding overhead) and handle
the error, or use a type check (so then custom objects won't be
usable).

-The C API frequently doesn't honour metamethods (lua_tonumber() for
example) or requires you to check types (again, there's
lua_isfunction() but not lua_iscallable()).

--
Sent from my Game Boy.


Rena,

These are great points and I'm glad that you brought them up. You even included the best reasons to need type and eluded to some of the limitations imposed by what's available.

I don't religiously check type. However, if the input is from the outside, I always check as much as I can. I don't *need* to do this and it does slow things down, but I do it for the error messages. Take this example:

```
function Class:method(foo_object)
---A bunch of code for several lines...
  local value = foo_object.content.attribute
--> error: attempt to index a nil value: field 'content' (or something like that)
```

 I hate these kinds of errors. They tell me nothing about what really happened, which is that the stupid calling function gave me a table and I expected to get a foo_object, or at least something that quack-foos.

I view the errors that Lua throws (attempt to index nil, attempt to concatenate a table value, etc) in the same way as I do asserts. Something is fundamentally broken in my implementation when they happen. When they do, I first write a more descriptive error message, then I fix my bug.

My point is that more than half of the time spent robustificating error messages, I'm checking input arguments to be sure that I blame the correct part of the stack. This mostly involves issues of capability (is_callable / is_callable or resumable / has a real tostring method) and classification. 

I know that one of the very few global functions that I've added as part of my "standard Lua" environment is an `is_callable` function.

On the other hand, it wasn't hard to write, either. :)

-Andrew