lua-users home
lua-l archive

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


чт, 18 июл. 2019 г. в 02:15, Hisham <h@hisham.hm>:
>
>
> 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.
>
> 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
>
Idea is good but syntax is inconsistent. It is too far from that it
should. <toclose> in for look nice but <toclose> in local is not.
Do you consider following scope prototype:

function io.scope()
    local m=setmetatable({ files={} },{index=io})
    function m.open(name,mode)
        local f,err=io.open(name,mode)
        if f then table.insert(m.files,f) end
        return f,err
    end
    function m.popen(name,mode)
        local f,err=io.popen(name,mode)
        if f then table.insert(m.files,f) end
        return f,err
    end
    local close_all=function()
        for i=#m.files,1,-1 do
            local f=m.files[i]
            m.files[i]=nil
            if io.type(f)=='file' then io.close(f) end
        end
    end
    m.close_all=close_all
    return function(ctx,prev)
        if prev==nil then
            return m
        else
            m.close_all() -- lua 5.3: will not work with break inside for
        end
    end,nil,nil,setmetatable({},{__close=close_all}) -- lua 5.4
end

-- usage:
for io in io.scope() do
    local s,serr=io.open("src","rb")
    local d,derr=io.open("dst","wb")
    if s and d then d:write(s:read()) end
end