lua-users home
lua-l archive

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


On Mon, 18 Aug 2014 08:02:01 +0200
Dirk Laurie <dirk.laurie@gmail.com> wrote:

> I don't like it. I don't like Lua 5.3.0-alpha ipairs either.
> 
> What you, and Lua 5.3.0-alpha, have been trying to do
> is to second-guess what semantics ipairs should have
> if a programmer has been tinkering with __len and __index.

That is just one part of my proposal. I basically copied it from
Lua 5.3.0-alpha (incorporating Roberto's idea regarding the
conditional call of luaL_len).

Please also consider the other aspect of my proposal to let
ipairs accept both functions and iterator triplets. This has
nothing to do with __len and __index.

> 
> And Lua 5.3.0-alpha is so prescriptive about it that the
> programmer does not even have the freedom to override
> that guess.

I don't like either that Lua 5.3.0-alpha is not allowing any
customization here. I feel like a standard interface for
"ordinal iteration over whatever object" would really help a
lot of programmers. Removing __ipairs takes away this ability
(IMHO leading to chaos, as elaborated earlier).

> 
> I think _any_ iterator that is metatable-aware must either
> be very explicitly specified (e.g. by __pairs or __ipairs)
> or stashed in the table library, which can have its own tight
> well-specified protocol.

Regarding tables, I would agree that it's a matter of taste
whether to interpret metamethods like __index and __len. I like
the interpretation Lua 5.3.0-alpha uses (disregarding the
previously discussed implementation issues).

But my proposal is mainly not about the interpretation of that
metamethods. It also treats functions in a specific way, which is
rather type-dependent behavior than having to do anything with
metatables.

If you consider my previously posted examples of supercool_ipairs
or the poweriterator C extension it also allows constructs like
this:


Consider a function printcsv, which accepts a sequence:

function printcsv(seq, sep)
  sep = sep or ","
  for i, v in ipairs(seq) do
    if i > 1 then io.stdout:write(sep) end
    io.stdout:write(tostring(v))
  end
  io.stdout:write("\n")
end

Calling this with a raw table is pretty much forward:

printcsv{"a", "b", "c"}
-- prints:
-- a,b,c

printcsv({"a", "b", "c"}, "; ")
-- prints:
-- a; b; c

But it's also possible to call it with a custom iterator function:

do
  local letter = nil
  local function alphabet()
    if letter == nil then
      letter = "a"
    elseif letter == "z" then
      return nil
    else
      letter = string.char(string.byte(letter) + 1)
    end
    return letter
  end
  printcsv(alphabet)
  -- prints:
  -- a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
end


Note that no __index or __len metamethod interpretation happens here.


You might want to ask why ipairs should accept iterator triplets.
Here is one example, why it's cool:

-- let's define a set using a raw table:
set = {apple=true, banana=true, guava=true}

-- now copy the set into an array by
-- making a nested ipairs(pairs(...)) call:
array = {}
for i, v in ipairs(pairs(set)) do array[i] = v end

-- print the array:
for i, v in ipairs(array) do print(i, v) end
-- prints:
-- 1   banana
-- 2   guava
-- 3   apple
-- (order of second column may vary)


All this got nothing to do with guessing what __index and __len mean.
It still works with the following simplified version of the extended
ipairs. The following code doesn't mention __index or __len:

do

  local function ipairsaux_raw(t, i)
    i = i + 1
    local v = rawget(t, i)
    if v then
      return i, v
    else
      return nil
    end
  end

  local function ipairsaux_func(f, i)
    local v, v2, v3, v4, vn = f()
    -- variable arg number requires C implementation
    if v then
      return i + 1, v, v2, v3, v4, vn
    else
      return nil
    end
  end

  local empty = {}

  function ipairs(x, s, i)
    local mt = getmetatable(x) or empty
    local mt_ipairs = rawget(mt, "__ipairs")
    if mt_ipairs ~= nil then
      return mt_ipairs(x)
    elseif type(x) == "function" then
      if s == nil and i == nil then
        return ipairsaux_func, x, 0
      else
        local n = 0
        return function()  -- closure not avoidable here
          n = n + 1
          local v, v2, v3, v4, vn = x(s, i)
          -- variable arg number requires C implementation
          if v == nil then
            return nil
          else
            i = v
            return n, v, v2, v3, v4, vn
          end
        end
      end
    else
      return ipairsaux_raw, x, 0
    end
  end

end


Regards
Jan