lua-users home
lua-l archive

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


On Mon, 15 Jul 2019 at 18:07, Sergey Kovalev <kovserg33@gmail.com> wrote:
>
> пн, 15 июл. 2019 г. в 23:01, Hisham <h@hisham.hm>:
> >
> > Have you ever a directory iterator? (e.g. for file in lfs.dir() ) If
> > so, you ran into this problem (maybe without realizing it).
> lua 5.4 solve this using toclose and 4-th parameter for "for"
> print(io.lines"readme.txt")
> function: 0x23af370    nil    nil    file (0x23af130)

I know, that was my point: to explain to Dirk why this feature is
making into Lua 5.4 iterators, and why it is something necessary and
not bloat.

> > So here's an idea for a third one, let's call it "resource for":
> >
> > stat ::=
> >    ...
> >    for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end |
> >    for namelist in explist do block end |
> >    for namelist ‘=’ exp do block end |
> >    ...
> >
> > (as you can see, it is syntactically a mix of the other two: you can
> > get multiple names on the LHS but a single expression on the RHS: if
> > it uses `=` but no `,`, it's a resource-for). It works in pseudo-code
> > like this:
> >
> > do
> >    local x, y, ... = exp
> >    if x == nil and y == nil then break end -- only auto-breaks if
> > _all_ values are nil, so we can use libraries which use nil-err
> > errors.
> >    block
> >    if x then
> >       local xmt = getmetatable(x)
> >       if xmt and xmt.__close then
> >          xmt.__close(x)
> >       end
> >    end
> > end
> >
> > Example usage:
> >
> > for fd, err = io.open("file", "r") do
> >    if err then
> >       print("failed!", err)
> >       break
> >    end
> >    print(fd:read("*a"))
> > end
>
> I think solution should cover typical usage scenarios. For example
> open source and destination files for processing and rise error if it
> impossible. "Happy ways" is shorter and simpler to write. Also it
> should be able to handle errors by user if he want it.
> src=auto(io.close){ io.open(src_filename,"rb") }
> dst=auto(io.close){ io.open(dst_filename,"wb") }
> -- do somethig with src and dst
>
>
> > You could read this as a "for that only runs once because it has no
> > iteration second argument".
> This could be done even now in lua 5.4

Yes, I know. In fact, it could be generalized: the <toclose> modifier
can be expressed purely in terms of the "closing value" support of the
generic-for in Lua 5.4. Consider this function, scoped():

----------------------------------------
function scoped(...)
   local cv = ...
   local args = table.pack(...)
   return function(_, v)
      if v == cv then
         cv = nil

         -- hack: force false instead of nil so that
         -- for runs in case of an error
         if args.n > 1 and args[1] == nil then
            args[1] = false
         end

         return table.unpack(args, 1, args.n)
      end
   end, nil, cv, cv
end
----------------------------------------

----------------------------------------
-- example use:
----------------------------------------
local fref

for f, err in scoped(io.open("scoped.lua")) do
   if err then
      print(err)
      break
   end
   fref = f
   print(f:read("*a"))
end

print(io.type(fref)) -- prints "closed file", hooray!
----------------------------------------

The above implementation, while it does demonstrate that <toclose> can
be expressed in terms of generic-for, is too heavyweight for
real-world use (with the extra closure and table packing and
unpacking). That's why I suggested adding it as a core language
feature instead of <toclose>.

Let's say I want to make the equivalent of the above program (have a
resource whose lifetime is scoped to a block) and use <toclose>. This
is honest-to-goodness the nicest way I managed to produce a version of
the above program using <toclose>, I'm not sure I like it better than
the one above:

----------------------------------------
local fref

do
   local f, err = io.open("scoped.lua")
   local <toclose> f = f    -- *****
   if err then
      print(err)
   else
      fref = f
      print(f:read("*a"))
   end
end

print(io.type(fref)) -- prints "closed file", hooray!
----------------------------------------

^ ***** to reiterate a previous post, it's really bad we need a
separate line for this. I might as well just write f:close() at the
end of the block. (It also throws off linters, which complain at the
shadowing.) If nothing else changes about to-be-closed variables
before Lua 5.4 final, I'd like at least this to be addressed.

I agree with you that one downside of this proposal is that it
requires a nesting level for each closable variable within scope (but
remember you can't make an array of closable variables, even with
<toclose>; you can make a closable array, though, as Gé suggested). On
the other hand, providing a visual cue about the lifetime of resources
is also an advantage.

-- Hisham