[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Standard Libraries:
- From: steve donovan <steve.j.donovan@...>
- Date: Mon, 21 Dec 2009 10:21:34 +0200
On Sat, Dec 19, 2009 at 10:36 PM, Mark Hamburg <mark@grubmah.com> wrote:
> There's always the option of having more namespaces.
>
> table.map( fn, t )
> iter.map( fn, iterFn, iterState, iterVal )
Yes, I went for this with Penlight, it seemed the best solution at the time:
http://penlight.luaforge.net/api/modules/pl.tablex.html
http://penlight.luaforge.net/api/modules/pl.seq.html
> From the standpoint of using the colon operator to handle the lookup thereby creating more generic code, one really wants table.map( t, fn ) which is fine but this doesn't generalize well to iterators since they involve up to three values.
Originally I prefered map( t, fn) because then anonymous functions
would hang out on the end, (like foreach) but it was pointed out that
map( fn, t ) is the canonical order. The clone of Python's list type
however did have the the method-friendly ordering which naturally did
allow method chaining, at the cost of insisting that arrays needed to
have the List metatable:
http://penlight.luaforge.net/api/modules/pl.list.html
I felt that forcing people to use an object type would not go down
well, so List is an optional convenience type, mostly implemented
using the tablex functions.
We agree that iterators (which I tended to call 'sequences') need
first-class treatment. There are a fair number of things which cannot
be done with tables, for instance, modeling unbounded lists
synchronized to real-time events. For instance, io.lines() over a
fifo pipe may frequently block; it's useful to have expressions like
this using sequence wrappers:
http://penlight.luaforge.net/#T31
seq(io.lines(mypipe)):take(10):upper():enum():map('..'):printall '\n'
That is, take the first 10 elements, apply the upper() method to all
elements, convert to a (key,value) sequence, concatenate these values,
and print out the lines:
1FIRST LINE
2SECOND
...
The interesting operation is the :upper() call, which is equivalent to
:map(string.upper). The idea is this: if a method is not found in the
Seq type, then assume that it is a method of the element type. If a
clear error message is produced in the case when this assumption
fails, then I can't see the harm in this shortcut.
The Seq type is of course callable and so these method chains can be
used in the for statement. I worried about the performance
implications, and wondered if we couldn't chain plain functions. And
we can, by modifying the metatable of all functions
> seq.import()
> lfs.dir('.'):printall()
. .. old out.txt readconfig.lua test.config test.ini ...
But this doesn't seem like a technique that has a place in large-scale
Lua because of uncontrolled global effects; useful perhaps for
scripting applications. (This is why at the time I was publically
wondering why functions could not get individual metatables)
Also, it turns out that one doesn't take too much of a hit going
through __call, so it was also perhaps premature optimization.
steve d.
PS. What would really make my Christmas is a reliable way of making
something like
seq(io.lines(mypipe)):take(10):upper():enum():map('..'):printall '\n'
run as a separate coroutine thread. I've recently been playing with
POSIX AIO and got some interesting things going, but AIO is a bit ...
odd on Linux.