kex docs Standard Library

Stream

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.

FS.File.feed hands back a stream of a file's lines, which is how to walk a file too large to hold in memory.

Methods

take

take(n: Integer) -> [A]

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.

Parameters
n
how many elements to produce

Returns 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)

drop

drop(n: Integer) -> Stream<A>

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.

Parameters
n
how many elements to skip

Returns 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

map(f: (A -> B)) -> Stream<B>

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.

Parameters
f
applied to each element

Returns 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

filter(pred: (A -> Bool)) -> Stream<A>

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.

Parameters
pred
the test applied to each element

Returns 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

each(f: (A -> Void)) -> Void

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.

Parameters
f
applied to each element
Examples
Stream.Sequence(from: 1) { |n| n + 1 }
  .map { |n| n * n }
  .toFeed
  .take(3)
  .each { |n| IO.printLine(n) }

toFeed

toFeed : Feed<A>

Returns a Feed that walks this stream once.

A stream remembers every element forced through it, so walking a long one holds all of it for as long as the stream is in scope. A feed drawn from it walks without holding the start, letting each element go as it passes — the way to consume a long stream in constant space.

Returns a one-shot cursor over the stream

Examples

Summing a long generated sequence without holding it

let total = Stream.Sequence(from: 1) { |n| n + 1 }
  .toFeed
  .take(1000000)
  .sum

module Stream

Constructors for Stream.

Sequence

Sequence(from: A, step: (A -> A)) -> Stream<A>

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.

Parameters
from
the first element
step
produces the next element from the current one

Returns 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]

Iterate

Iterate(seed: A, step: (A -> A)) -> Stream<A>

Builds an infinite stream from a seed and a step function. The same thing as Sequence — use whichever reads better where you are.

Parameters
seed
the first element
step
produces the next element from the current one

Returns the generated stream

Examples
let odds = Stream.Iterate(1) { |n| n + 2 }
odds.take(5)   # => [1, 3, 5, 7, 9]

empty constant

The stream with no elements.

Examples
Stream.empty.take(3)   # => []