I have a very simple problem with a simple iterator.
Let's say that I design a function, files()
, that iterates over all the files in a folder:
for file in files("/path/to/folder") do
print(file)
end
Now, this seems perfect, but there's a problem here: What if the the folder doesn't exist, or we don't have read permission to it?
How would we indicate such error?
One solution would be to have files()
return nil, "no read permission"
in this case. We'd then be able to wrap the call to files()
inside assert()
:
for file in assert(files("/path/to/folder")) do
print(file)
end
This seemingly solves the problem. But this forces our users to always use assert()
. What if the user doesn't care about errors? For this kind of users we'd want our files()
to behave as if the folder is empty. But Lua --in case files()
indicates error-- would try to call the returned nil
and this will result in an error ("attempt to call a nil value").
So,
How can we design an iterator, files()
, that would cater to both users that care about errors and users that don't?
If it's not possible, what alternative would you suggest?
First: Instead of returning nil
+ error message consider raising an error in the files
function (using error
). This way you can't forget the assert
call, and you won't get the confusing "attempt to call a nil value" error.
You could pass an extra boolean parameter to files
when you don't want to raise errors -- you should return an empty function (function() end
) instead of calling error
in this case.
A more general approach is the following:
-- an iterator that immediately stops a for loop
local function dummy_iter() end
-- catch errors and skip for loop in that case
function iterpcall( g, ... )
local ok, f, st, var = pcall( g, ... )
if ok then
return f, st, var
else
return dummy_iter
end
end
for file in iterpcall( files, "/path/to/folder" ) do
print( file )
for line in iterpcall( io.lines, file ) do -- works for other iterators as well
print( line )
end
end
The implementation of iterpcall
above only handles errors raised in the iterator generator (files
or io.lines
), not in the iterator function (f
) itself. You would have to wrap f
in a closure with a pcall
to do that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With