[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Why is implicit and explicit 'nil' treated differently?
- From: Andrew Starks <andrew.starks@...>
- Date: Sat, 18 Apr 2015 17:19:51 -0500
On Saturday, April 18, 2015, Jan Behrens <jbe-lua-l@public-software-group.org> wrote:
On Sat, 18 Apr 2015 21:45:01 +0300
<tonyp@acm.org> wrote:
> As you can see in the sample code below, print does not show the
> implicit nil (when it is last in the list of things to print), yet it
> shows the explicit one. However, in every other regard, nil seems to
> behave the same in both cases.
>
> Can someone explain why there is a difference in 'nil' treatment
> between these two? (I can see the compiler produces slightly
> different code for each case but if they are equivalent, shouldn?t it
> produce the exact same code for both cases?)
It will be shown if you write:
print(implicit()==nil, implicit(), implicit() or 'hidden')
--> true nil hidden
or
print(implicit()==nil, implicit() or 'hidden', (implicit()))
--> true hidden nil
Generally "nil" and "none" (none = no value) are distinct when you call
a function or return from a function. However, "none" gets implicitly
converted to "nil" if you store it in a variable (or table) or if you
put parenthesis around it (or use it in an _expression_ where a single
value is expected).
If you use a function call as last(!) argument to another function
call, e.g. f(arg1, arg2, g()), then multiple arguments returned from
the last function will be passed to the outer function:
function g()
return "Hello", "World"
end
print("Note:", g())
--> Note: Hello World
That's why
print(implicit()==nil, implicit() or 'hidden', implicit())
won't print "nil", because implicit() is last in the argument list,
which means that any variable number of return values (possibly zero
return values) will be passed to the print function in addition to the
two first arguments.
>
> And, is the difference evident only in print or elsewhere also?
I think "print" and "select" are the two notable cases. You can use
"select" to write your own functions to distinguish between "nil" and
"none":
function test(...)
if select("#", ...) == 0 then
return "no value given"
elseif ... == nil then
return "nil value given"
else
return "non-nil value given"
end
end
test()
--> no value given
test(nil)
--> nil value given
test(false)
--> non-nil value given
>
> I?m mostly interested if I should expect the exact same execution
> path for code receiving either nil, or not? Because obviously print
> has a different execution for each case.
If you store a return value in a local variable or table, then "none"
always gets automatically converted to "nil". If you want to perform
this conversion explicitly, use parenthesis. This is also helpful to
truncate a variable number of return values. Compare:
print("Pos:", string.find("Hello World", "World"))
--> Pos: 7 11
print("Pos:", (string.find("Hello World", "World")))
--> Pos: 7
>
> Thank you.
Kind Regards,
Jan Behrens
>
> function implicit()
> return --implicit nil
> end
>
> function explicit()
> return nil --explicit nil
> end
>
> print(implicit()==nil,implicit() or 'hidden',implicit())
> print(explicit()==nil,explicit() or 'shown',explicit())
> print(implicit() == explicit())
>
> --- And here?s what the compiler output looks like for the two
> functions ---
>
> function <implicit> (2 instructions at 004CC030)
> 0 params, 2 slots, 0 upvalues, 0 locals, 0 constants, 0 functions
> 1 [2] RETURN 0 1
> 2 [3] RETURN 0 1
>
> function <explicit> (3 instructions at 004CE790)
> 0 params, 2 slots, 0 upvalues, 0 locals, 0 constants, 0 functions
> 1 [6] LOADNIL 0 0
> 2 [6] RETURN 0 2
> 3 [7] RETURN 0 1
There is one other case where nil/none appear. It almost never matters, but it thwarted me from being too creative with tables: __index is always adjust to one return value. So:
__index = function(t, i, x)
--x is always absent (empty/nil), btw. This would almost certainly never matter, but it's true. I'm not sure how I know this,
actually.it wouldn't happen from trying `t[1, 2]`, because that's an error. Anyway...
end
f = function()
end
print (select("#", __index()), select("#", f()))-->1 0
So you can never signal the difference between nothing and `nil` from your index metamethod. Again, not complaining, I'm just sayin'.