• Subject: Re: [ANN] Penlight Lua Libraries
• From: David Manura <dm.lua@...>
• Date: Sat, 25 Apr 2009 03:17:07 -0400

```On Thu, Apr 23, 2009 at 10:01 AM, steve donovan wrote:
>> Penlight is a set of pure Lua libraries designed to make some common
>> tasks easier and more standard.

Thank you.  The tutorial is especially useful.  I feel that a lot of
modules don't well advertise themselves in terms of how the module is
intended to be used in context with examples and design comments.
Sometimes the docs are hidden in tarballs.

Incidentally, I've been back working on Perl, were some algorithmic
tasks are so convenient, and then returning to Lua, which though as
good as Lua is, there's a lack of these building blocks such as you've
developed.  In that light, here's a few "common patterns" of my own
and analysis of how well Penlight addresses them.

For example, I wanted to print a table, sorting by keys.  In Perl we just do

for my \$k (sort keys %t) {
print "\$k,\$t{\$k}\n";
}

In Lua, using only standard libraries, we have code that is a bit
removed from the problem domain:

local ks = {}
for k in pairs(t) do ks[#ks+1] = k end
table.sort(ks)
for _,k in ipairs(ks) do
print(k,t[k])
end

With some helpful utility functions though, such as in Penlight, this
is clarified to

local ks = tablex.keys(t)
table.sort(ks)
for k in seq.list(ks) do
print(k,t[k])
end

If the sort didn't have to be in-place, we could chain the
transformations on a single line:

for k in seq.list(tablex.sort(tablex.keys(t))) do
print(k,t[k])
end

That's not too bad.  A minor point is that the parens start to
accumulate in the chain.  We might use compose if you extended it to
take more than two arguments:

for k in func.compose(seq.list, tablex.sort, tablex.keys)(t) do
print(k,t[k])
end

We can also do this nicely with sequences:

for k in seq.sort(seq.all_keys(t)) do
print(k,t[k])
end

Alternately, this can be done with lists:

local ks = List(tablex.keys(t))
ks:sort()
for k in pl.list.iter(ks) do
print(k,t[k])
end

Similarly, it would be nice to have the sort allowed to not be in-place:

for k in pl.list.iter(List(tablex.keys(t)):sort()) do
print(k,t[k])
end

That's a bit ugly.  We're dealing with table arrays, lists, and
sequences.  I feel there's a potential for confusion between these
three.  The distinction between the three is not inherent to the
problem domain (ordered sequences), yet the programmer has to decide
which of these to use and how to fit them together.

Another recent example I had was to select one element from the list
1..10 that matched a predicate f but was not in the list t.
Mathematically, {x in 1..10 : f(x) and x not in t}.

In Perl, it's fairly concise:

my %s = map {\$_ => 1} @t;
my (\$x) = grep { ! \$s{\$_} && f(\$_) } (1..10);

Using only the Lua standard library, we have a bit of coding to do:

local function makeset(t)
local s = {}
for _,v in ipairs(t) do s[v] = true end
return s
end
local s = makeset(t)
local x
for i=1,10 do if not s[i] and f(i) then
x = i
end end

With library or utility functions, we obtain

local s = tablex.index_map(t)
local x = tablex.filter(List.range(10):map(_1+1), function(i) return
not s[i] and f(i) end)[1]

The above could be simplified if range accepted bounds, as it does in
sequence form:

local s = tablex.index_map(t)
local x = seq.filter(seq.range(1,10), function(i) return not s[i]
and f(i) end)()

which is not too bad.  Perhaps some syntax support for list
comprehensions could make it a bit cleaner.

I'm not certain I like your argument order for functions though.  As
the docs say,

<quote>
...and functions which take a function argument (like tablex.map)
will have the function as the last argument, because of the syntax of
Lua anonymous functions:

s = tablex.map(t,function(x)
return x*x
end)
</quote>

```