tested index patch (was: Undefined variables returning nil)

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

tested index patch (was: Undefined variables returning nil)

Sven Olsen
Lua's behavior of silently returning nil for undefined variables can lead to bugs that propagate undetected.
 
Maybe there's an interesting little parser patch here.  Say a piece of sugar that converts

   object!.name

to

  (object.name or error("'object' lacks required field 'name'"))

This is the kind of shorthand that's easy to add to the parser, and it seems like it would be handy.  

Of course, now that I've started thinking about it, I'm trying to figure out if I can define a version of the operator that would work well with Peter's table unpack semantic.  Specifically, I'd like to throw informative errors for statements like these:

   local name,size in object!

And that requires a bit more trickery.  At the moment, a transformation along the lines of object! --> (object or _MISSING.object) appears pretty promising.

If anyone else wants this kind of syntax sugar for their own scripts, let me know.  I may have just gone ahead and rolled a quick patch :-)

-Sven
Reply | Threaded
Open this post in threaded view
|

Re: tested index patch (was: Undefined variables returning nil)

Peter Loveday
> Maybe there's an interesting little parser patch here. Say a piece of sugar that converts
> object!.name
> to
> (object.name or error("'object' lacks required field 'name'"))
 
“or” isn’t quite what is wanted here, is it?  it would (undesirably) catch object.name == false (as opposed to just nil).
 
- Peter
Reply | Threaded
Open this post in threaded view
|

Re: tested index patch (was: Undefined variables returning nil)

Sven Olsen


“or” isn’t quite what is wanted here, is it?  it would (undesirably) catch object.name == false (as opposed to just nil).


Yes, "or" is clearly wrong.  Something like object! -> _CHECKED("object", object) might work.  I'd hoped for a more elegant definition, but, it looks like I fell victim to my own cleverness.

-Sven 
Reply | Threaded
Open this post in threaded view
|

Re: tested index patch (was: Undefined variables returning nil)

Sven Olsen
 Something like object! -> _CHECKED("object", object) might work.

Hrm.  The object! -> _CHECKED( "object", object) approach is awkward for a variety of reasons.  

So I'm still probably trying to be too clever here.  A "tested index" semantic would be straightforward enough if I gave up on making it compatible with Peter Shook's table unpack syntax.  The best approach is probably just to make an operator that handles the field selection case exclusively. I.e., one who's only purpose is to error properly on 'table!.key'.  Then the tested table unpack use case could be handled with an independent operator, like 'vars in! table'.

-Sven
Reply | Threaded
Open this post in threaded view
|

Re: tested index patch (was: Undefined variables returning nil)

Sven Olsen
“or” isn’t quite what is wanted here, is it?  it would (undesirably) catchobject.name == false (as opposed to just nil).

Ok, now it's starting to look like I've got myself a fairly clean, simple patch.  

   table!.field   

is syntax sugar for
   
   _CHECKED(table.field,"table","field")

where _CHECKED is defined as:

function _CHECKED(v,tname,kname)
if v~=nil then
return v
end
error(string.format("%s is missing required field: %q",tname or "<expression>", kname),2)
end

In the case that there's no clear name string associated with the table, then we can make the syntax sugar pass nil as the second argument to checked.  So, for example,

local a = (b or c)!.e

expands as:

local a =_CHECKED( (b or c).d, nil, "d" )

That's a limited semantic which seems to work fine in practice.  You can't use "table!.value" as an lvalue, but that's probably not a big deal, as most of the use cases I can think of are reads.

-Sven
Reply | Threaded
Open this post in threaded view
|

Re: tested index patch (was: Undefined variables returning nil)

Tim Hill
I must be missing something, but why can't you just do this with a metatable and skip the syntactic sugar altogether?

--Tim

On Mar 22, 2013, at 12:11 AM, Sven Olsen <[hidden email]> wrote:

“or” isn’t quite what is wanted here, is it?  it would (undesirably) catchobject.name == false (as opposed to just nil).

Ok, now it's starting to look like I've got myself a fairly clean, simple patch.  

   table!.field   

is syntax sugar for
   
   _CHECKED(table.field,"table","field")

where _CHECKED is defined as:

function _CHECKED(v,tname,kname)
if v~=nil then
return v
end
error(string.format("%s is missing required field: %q",tname or "<expression>", kname),2)
end

In the case that there's no clear name string associated with the table, then we can make the syntax sugar pass nil as the second argument to checked.  So, for example,

local a = (b or c)!.e

expands as:

local a =_CHECKED( (b or c).d, nil, "d" )

That's a limited semantic which seems to work fine in practice.  You can't use "table!.value" as an lvalue, but that's probably not a big deal, as most of the use cases I can think of are reads.

-Sven

Reply | Threaded
Open this post in threaded view
|

Re: tested index patch (was: Undefined variables returning nil)

Sven Olsen

I must be missing something, but why can't you just do this with a metatable and skip the syntactic sugar altogether?

Well, the thought is that a specialized syntax could provide a useful degree of fine grain control.  Whether or not to throw an error in response to an undefined reference isn't necessarily a natural a property of tables. 

In some ways a '!.' syntax feels like the natural inverse of the "safe navigation" sugar I posted a few weeks back.  In situations where silently returning nil is desirable, you can write:

a=node?.parent?.name

But if the situation recommends loud failures, you'd write:

a=node.parent!.name

In both cases, the error handling behavior is specified by the syntax of the call, rather than being stored as a trait of node.parent or the nil metatable.

But while the safe navigation patch is certainly one that I like, I'm not yet convinced that it's inverse is really worth the trouble.  In a moment of procrastination, I went ahead and wrote the feature into my own parser -- now I'll live with it for a few months and see if it proves useful in practice.

-Sven