0.45+9.1.0.1

3.8 Streams🔗ℹ

A stream is like a sequence, but it is stateless and supports functional “first” and “rest” operations like a linked list. Lists and sequence ranges can be used as streams. Any sequence can be turned into a stream using Sequence.to_stream, which instantiates the sequence an caches results so that the same first value can be provided from a stream as many times as it is requested. An element can be accessed from a stream using [], which is equivalent to accessing the Stream.rest property as many times as the index in [], and then using Stream.first to get the element.

The Stream.cons expression form is similar to List.cons, but it constructs a lazy stream: the expressions for the first and rest of the stream are evaluated on demand. A classic example for lazy streams is creating an infinite stream of numbers. Although ranges can also represent streams of numbers, they cannot represent, say, a stream of prime numbers.

fun naturals_from(i :: Int) :: Stream:

  Stream.cons(i, naturals_from(i+1))

def naturals = naturals_from(0) // equivalent to `(0 ..)`

> naturals.first

0

> naturals.rest.first

1

> naturals[0]

0

> naturals[11]

11

fun remove_multiples(s :: Stream, n :: Int) :: Stream:

  match s

  | Stream.cons(fst, rst):

      if fst mod n == 0

      | remove_multiples(rst, n)

      | Stream.cons(fst, remove_multiples(rst, n))

def odds = remove_multiples(naturals, 2)

> odds[0]

1

> odds[11]

23

fun primes_from(s :: Stream) :: Stream:

  match s

  | Stream.cons(fst, rst):

      Stream.cons(fst, primes_from(remove_multiples(rst, fst)))

def primes = primes_from(naturals.rest.rest)

> primes[0]

2

> primes[11]

37