Behavior of load() when chunk function returns nil
Asked Answered
S

2

6

From the Lua 5.1 documentation for load():

Loads a chunk using function func to get its pieces. Each call to func must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk.

From my testing, this is not actually true. Or, rather, the documentation is at a minimum misleading.

Consider this example script:

function make_loader(return_at)
    local x = 0

    return function()
        x = x + 1

        if x == return_at then return 'return true' end

        return nil
    end
end

x = 0
repeat
    x = x + 1
until not load(make_loader(x))()

print(x)

The output is the number of successive calls to the function returned by make_loader() that returned nil before load() gives up and returns a function returning nothing.

One would expect the output here to be "1" if the documentation is to be taken at face value. However, the output is "3". This implies that the argument to load() is called until it returns nil three times before load() gives up.

On the other hand, if the chunk function returns a string immediately and then nil on subsequent calls, it only takes one nil to stop loading:

function make_loader()
    local x = 0

    return {
        fn=function()
            x = x + 1

            if x == 1 then return 'return true' end

            return nil
        end,
        get_x=function() return x end
    }
end

loader = make_loader()
load(loader.fn)
print(loader.get_x())

This prints "2" as I would expect.

So my question is: is the documentation wrong? Is this behavior desirable for some reason? Is this simply a bug in load()? (It seems to appear intentional, but I cannot find any documentation explaining why.)

Sporocyst answered 5/6, 2013 at 20:19 Comment(2)
Have you tried Lua 5.2?Hypoploid
You might want to try it. It's probably just a bug in Lua 5.1.Hypoploid
B
6

This is a bug in 5.1. It has been corrected in 5.2, but we failed to incorporate the correction in 5.1.

Bulldoze answered 6/6, 2013 at 13:23 Comment(0)
K
3

I get slightly different results from yours, but they are still not quite what the documentation implies:

function make_loader(return_at)
    local x = 0
    return function()
        x = x + 1
        print("make_loader", return_at, x)
        if x == return_at then return 'return true' end
        return nil
    end
end

for i = 1, 4 do
    load(make_loader(i))
end

This returns the following results:

make_loader 1   1
make_loader 1   2
make_loader 2   1
make_loader 2   2
make_loader 2   3
make_loader 3   1
make_loader 3   2
make_loader 4   1
make_loader 4   2

For 1 it's called two times because the first one was return true and the second one nil. For 2 it's called three times because the first one was nil, then return true, and then nil again. For all other values it's called two times: it seems like the very first nil is ignored and the function is called at least once more.

If that's indeed the case, the documentation needs to reflect that. I looked at the source code, but didn't see anything that could explain why the first returned nil is ignored (I also tested with empty string and no value with the same result).

Kneecap answered 5/6, 2013 at 21:41 Comment(1)
+1 Good info. Not sure if it's technically an answer and so I won't mark it as such, but the additional information is definitely useful.Sporocyst

© 2022 - 2024 — McMap. All rights reserved.