Pull-based Streams for Lua
This is the unstable main branch.
Please, switch to stable v0.2.
[ About | Install & Run | API | Examples ]
f-streams is a pull-based streams library for Lua:
- A stream produces a new value each time is called.
- A stream terminates when it returns
nil. - A stream can use
:combinators to create stream pipelines. - A stream can be iterated over using Lua's generic for loop.
- A stream can represent infinite lazy lists.
The following example prints the first three odd numbers multiplied by 2:
local S = require "streams"
S.from(1) -- 1, 2, 3, ...
:filter(function (x) return x%2 == 1 end) -- 1, 3, 5, ...
:map(function (x) return x * 2 end) -- 2, 6, 10, ...
:take(3) -- 2, 6, 10
:tap(print) -- 2 / 6 / 10
:to()
sudo luarocks install f-streams
lua <example.lua>
You may also copy the file streams/init.lua as streams.lua into your Lua
path, e.g.:
cp streams/init.lua /usr/local/share/lua/5.4/streams.lua
The API is divided into three groups: sources, combinators and sinks.
A source has the prefix fr_ and creates a stream from the given values.
A combinator combines streams and values to create new streams.
A sink has the prefix to_ and consumes a stream, producing results, until it
terminates.
-
Sources
fr_const(v): stream of constantsvfr_coroutine(co): stream of values from coroutinecofr_counter(a): stream of numbers fromato infinityfr_function(f): stream off()resultsfr_range(a,b): stream of numbers fromatobfr_streams(...): stream of values from each stream in...fr_table(t): stream of values fromtfrom(v): calls the appropriatefr_*forv
-
Combinators
acc0(s,z,f): starting withz, accumulates each value ofsbased onf:v0=z, v1=f(z,s()), v2=f(v1,s()), ...acc1(s,f): accumulates each value ofsbased onf:v1=f(nil,s()), v2=f(v1,s()), ...empty(): an empty streamfilter(s,f): filterssbased onfmap(s,f): appliesfto each value ofs:f(s()), f(s()), ...mapi(s,f): appliesfto each indexed value ofsf(1,s()), f(2,s()), ...max(s): maximum between each value ofsmin(s): minimum between each value ofsmul(s): multiplies each value ofsseq(s1, s2): all values ofs1followed by all values ofs2skip(s,n): skips the firstnvalues ofssum(s): sums each value ofstable(s): accumulates each value ofsinto an increasing table:{}, {s()}, {s(),s()}, ...take(s,n): takes the firstnvalues ofstap(s,f): appliesfto each value ofstee(s): splitssin two- tee(s,n)
: splitssinn` - tee(s,...)
: splitssin as manyFiin..., applyingFi(s)`
- tee(s,n)
xseq(ss): flattens a stream of streamsssinto a single streamzip(...): zips allSiin...as a stream of tuples{S1,S2,...}
-
Sinks
to(s): same asto_last(s)to_all(s,f): if all values ofsconform withfto_any(s,f): if any value ofsconforms withfto_first(s): first value ofsto_last(s): last value ofsto_none(s,f): if no values ofsconform withfto_some(s,f): if multiple values ofsconform withf
-
Other
S.is(s): returns ifsis a stream
As a fundamental limitation, f-streams does not support a merge
combinator to read from multiple streams concurrently.
However, this limitation is addressed by lua-atmos, which
extends f-streams with equivalent combinators.
Produces the values 1, 2, and 3:
local S = require "streams"
local s = S.from(1,3)
print(s()) -- 1
print(s()) -- 2
print(s()) -- 3
print(s()) -- nil
Counts from 1 to infinity, takes the first 3 values, converts to table, and
print all indexes and values:
-- without `:` notation
cnt = S.fr_counter() -- 1, 2, 3, 4, 5, ...
vs3 = S.take(cnt, 3) -- 1, 2, 3
tab = S.table(vs3) -- {1}, {1,2}, {1, 2, 3}
ret = S.to(tab) -- {1, 2, 3}
for i,v in ipairs(ret) do
print(i,v) -- 1,1 / 2,2 / 3,3
end
From a table with names, prints all starting with J:
js = S.from { "Joao", "Jose", "Maria" }
:filter(function(n) return n:find("^J") end)
for n in js do
print(n) -- Joao / Jose
end
Prints each value from 1 to 10:
S.from(1,10):tap(print):to()