lua-users home
lua-l archive

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




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'.