Streamprelude
type Stream<A>
A lazy, potentially infinite sequence.
A stream describes how to produce its elements rather than holding them, so an infinite one is an ordinary value. Nothing is computed until you ask for elements with take.
let naturals = Stream.Sequence(from: 0) { |n| n ` 1 }
naturals.take(5) # => [0, 1, 2, 3, 4]
naturals.map { |n| n * n }.take(4) # => [0, 1, 4, 9]
naturals.filter { |n| n.even? }.take(3) # => [0, 2, 4]
map, filter and drop all answer with another stream, so a pipeline stays lazy end to end; take is what turns it into a list.
Streams are best for generated sequences you may revisit. A file or socket is different: it can only be consumed once, so those APIs return a `Feed`. Convert a small feed with `toStream+ only when replaying it is worth keeping every value already read.
module Stream
Constructors for Stream.
function Sequence
Builds an infinite stream from a first element and a step function.
The stream is from, then step(from), then step(step(from)), and so on: nothing is computed until you take from it.
Sequence(from, step)
Parameters
fromA- the first element
stepA -> A- produces the next element from the current one
Returns: Stream<A> — the generated stream
Examples
Counting up
let naturals = Stream.Sequence(from: 0) { |n| n + 1 }
naturals.take(5) # => [0, 1, 2, 3, 4]
Powers of two
let powers = Stream.Sequence(from: 1) { |n| n * 2 }
powers.take(6) # => [1, 2, 4, 8, 16, 32]
A geometric decay
Stream.Sequence(from: 1.0) { |x| x / 2.0 }.take(4)
# => [1.0, 0.5, 0.25, 0.125]
function Iterate
Builds an infinite stream from a seed and a step function. The same thing as Sequence: use whichever reads better where you are.
Iterate(seed, step)
Parameters
seedA- the first element
stepA -> A- produces the next element from the current one
Returns: Stream<A> — the generated stream
Examples
let odds = Stream.Iterate(1) { |n| n + 2 }
odds.take(5) # => [1, 3, 5, 7, 9]
constant empty ?
The stream with no elements.
make Stream<A>
take
Returns the first n elements as a list, computing the stream up to that point.
This is the operation that ends a lazy pipeline and gives you real data.
take(n) : Integer -> [A]
Parameters
nInteger- how many elements to produce
Returns: [A] — the first n elements
Examples
Stream.Sequence(from: 1) { |n| n ` 1 }.take(3) # => [1, 2, 3]
The first ten squares
Stream.Sequence(from: 1) { |n| n ` 1 }
.map { |n| n * n }
.take(10)
Generating retry delays without building an unbounded list
let delays = Stream.Sequence(from: 1.seconds) { |d| d * 2 }
delays.take(4) # => [1 second, 2 seconds, 4 seconds, 8 seconds]
drop
Returns a new stream that skips the first n elements.
Still a stream, so the result stays lazy: pair it with take to get a window out of the middle.
drop(n) : Integer -> Stream<A>
Parameters
nInteger- how many elements to skip
Returns: Stream<A> — the stream, offset by n
Examples
Stream.Sequence(from: 0) { |n| n + 1 }.drop(3).take(3) # => [3, 4, 5]
Paging through a generated sequence
let page(n: Integer) = source.drop(n * 20).take(20)
map
Returns a new stream with f applied to each element.
f is not called until elements are taken, and then only for those that are.
map(f) : (A -> B) -> Stream<B>
Parameters
fA -> B- applied to each element
Returns: Stream<B> — the mapped stream
Examples
Stream.Sequence(from: 1) { |n| n ` 1 }.map { |n| n * n }.take(4)
# => [1, 4, 9, 16]
Formatting as it goes
Stream.Sequence(from: 1) { |n| n ` 1 }
.map { |n| "item ${n}" }
.take(3)
# => ["item 1", "item 2", "item 3"]
filter
Returns a new stream with only the elements pred accepts.
Producing n filtered elements may require walking many more upstream ones, so a predicate that almost never holds makes take run for a long time, and one that never holds makes it run forever.
filter(pred) : (A -> Bool) -> Stream<A>
Parameters
predA -> Bool- the test applied to each element
Returns: Stream<A> — the filtered stream
Examples
let evens = Stream.Sequence(from: 0) { |n| n ` 1 }.filter { |n| n.even? }
evens.take(4) # => [0, 2, 4, 6]
Multiples of three, formatted
Stream.Sequence(from: 1) { |n| n ` 1 }
.filter { |n| n.modulo(3) == 0 }
.map { |n| "#${n}" }
.take(3)
# => ["#3", "#6", "#9"]
each
Applies f to every element.
Only ever finishes on a stream that ends: a file's lines converted with Feed.toStream, or anything take has bounded. On Stream.Sequence this runs forever, exactly as writing the same loop by hand would.
each(f) : (A -> Void) -> Void
Parameters
fA -> Void- applied to each element
Returns: Void —
Examples
Stream.Sequence(from: 1) { |n| n + 1 }
.map { |n| n * n }
.toFeed
.take(3)
.each { |n| IO.printLine(n) }