Link Search Menu Expand Document

Oters - Streams


I have already introduced streams in the previous sections but since they are such a central concept when programming with Oters, I would like to dedicate an entire page to explaining how they work. We can think of streams as a tuple, with the first element being the value available now, and the second element being a reference to the rest of the stream available in the next time step:

\[Stream\ A \equiv (A,\ \bigcirc (Stream\ A))\]

We can also think of the second element as being a computation to be done in the next time step that will update the stream to the next pair. Take the following example for the stream of natural numbers:

let from = fn n -> n << @(from (n+1))
let nats = from 0

The function from constructs a stream with the << operator. We set the current value of the stream to be the input n, and then we say that in the next time step (marked by @), recompute from but passing n+1 as input instead. Thus, we get a recursive definition for a stream.

Streams are useful because they capture the behavior of GUIs very intuitively. A mouse input for instance can be represented as a stream of (int, int) pairs. Graphical elements can also be represented as streams: A button is a stream transformer that takes a stream of labels (Stream<string>) and outputs a stream of booleans that indicate whether the button was pressed in the current time step.

The “magic” of Oters is in the streams. Simple declarative definitions like the one above produce fairly complex behavior. The interpreter automatically updates streams an performs the appropriate computations at each time step for every stream. Thus, the nats stream above produces the sequence of natural numbers one at a time, synchronized with the rest of the program.

There is one caveat. For a stream to be automatically updated by the interpreter, it must be bound to a variable. Thus, let _ = from 0 would not create a proper stream. While this example makes no difference, for streams with side effects, such as those created by UI widgets, it is important that they are bound to a variable. So while the function gui::widget::label returns a “useless” Stream<()>, it is important that it is bound to a variable in order to draw the label on screen.

Events

A lot of the behavior we have in GUIs can also be represented as Events. We often want to produce some change as a response to an event (e.g. a mouse click or a key press). Events in Oters are simply defined as streams of Options. None values tell us that the event hasn’t occurred in this time step, and Some x values tell us that the event occurred now and it has a value x. Many FRP functions take events as inputs to produce a change in the state of the program. We will see more of this in the standard library.