Reaxive.Rx

This module implements the combinator on reactive streams of events.

The functionality is closely modelled after Reactive Extensions and after ELM. However, names of function follow the tradition of Elixir’s Enum and Stream modules, if applicable.

See the test cases in rx_test.exs for usage patterns.

Source

Summary

all(rx, pred)

Returns true if pred holds for all events in the sequence

any(rx, pred)

Returns true if pred holds for at least one event in the sequence

as_text(rx)

This is a simple sink for events, which can be used for debugging event streams. It writes all events to standard out

delayed_start(generator, id \\ "delayed_start", timeout \\ 5000)

The delayed_start function starts a generator after the first subscription has arrived. The generator gets as argument rx the new creately Rx_Impl and sends is internally encoded values via

distinct(rx)

The distinct transformation is a filter, which only passes values that it has not seen before. Since all distinct values has to be stores inside the filter, its required memory can grow for ever, if an unbounded sequence is used

distinct_until_changed(rx)

The distinct_until_changed transformation is a filter, which filters out all repeating values, such that only value changes remain in the event sequence

drop(rx, n)

drop filters out the first n elements of the sequence

drop_while(rx, pred)

drop_whilefilters out the first elements while the predicate istrue`

empty(timeout \\ 5000)

Creates the empty sequence of events. After a subscription, the sequence terminates immediately

error(exception, timeout \\ 5000)

The error function takes an in Elixir defined exception and generate a stream with the exception as the only element. The stream starts after the first subscription

eval(exp)

Evaluates a lazy expression, encoded in Rx.Lazy. Returns the argument if it is not an Rx.Lazy encoded

filter(rx, pred)

This function filter the event sequence such that only those events remain in the sequence for which pred returns true

first(rx)

The first element of the event sequence. Does return the first scalar value and dispose the event sequence. The effect is similar to

flat_map(rx, map_fun)

The flat_map function takes a mapping function and a sequence of events. It applies the map_fun to each source event. The map_fun is a function which must return a sequence of events (i.e. an Observable). All resulting sequences are then flattened, such that only one sequence of events is returned from flat_map. Since all sequences generated by the mapping process are running concurrently to each other, the flattening process does not ensure any ordering of the resulting event sequence

generate(collection, delay \\ 0, timeout \\ 5000)

The generate function takes a collection and generates for each element of the collection an event. The delay between the events is the second parameter. The delay also takes place before the very first event

lazy(expr)

This macros suspends an expression and replaces is with an Rx.Lazy thunk

map(rx, fun)

The map functions takes an observable rx and applies function fun to each of its values

merge(rxs)
merge(rx1, rx2)

Merges two or more event sequences in a non-deterministic order

naturals(delay \\ 0, timeout \\ 5000)

Generates all naturals numbers starting with 0

never()

The never function creates a sequence of events that never pushes anything

product(rx)

Multiplies all events of the sequence and returns the product as number

reduce(rx, acc, reduce_fun)

This function considers the past events to produce new events. Therefore this function is called in ELM foldp, folding over the past

return(value)

The return function takes a value and creates an event sequence with exactly this value and terminates afterwards

start_with(prev_rx, collection)

The function start_with takes a stream of events prev_rx and a collection. The resulting stream of events has all elements of colletion, followed by the events of prev_rx

stream(rx)

Converts a sequence of events into a (infinite) stream of events

stream_observer(pid \\ :erlang.self())

A simple observer function, sending tag and value as composed message to the process

sum(rx)

Sums up all events of the sequence and returns the sum as number

take(rx, n)

This function produces only the first n elements of the event sequence. n must be positive

take_until(rx, pred)

Takes the first elements of the sequence until the predicate is true

take_while(rx, pred)

Takes the first elements of the sequence while the predicate is true

to_list(rx)

Converts the event sequence into a regular list. Requires that the sequence is finite, otherwise this call does not finish

Functions

all(rx, pred)

Specs:

Returns true if pred holds for all events in the sequence.

Examples

iex> alias Reaxive.Rx
iex> require Integer
iex> Rx.naturals |> Rx.take(10) |> Rx.map(&(1+&1*2)) |> Rx.all &Integer.is_odd/1
true
Source
any(rx, pred)

Specs:

Returns true if pred holds for at least one event in the sequence.

Examples

iex> alias Reaxive.Rx
iex> require Integer
iex> Rx.naturals |> Rx.take(10) |> Rx.any fn(x) -> x > 5 end
true
Source
as_text(rx)

Specs:

This is a simple sink for events, which can be used for debugging event streams. It writes all events to standard out.

Source
delayed_start(generator, id \\ "delayed_start", timeout \\ 5000)

Specs:

The delayed_start function starts a generator after the first subscription has arrived. The generator gets as argument rx the new creately Rx_Impl and sends is internally encoded values via

Observer.on_next(rx, some_value)

All other functions on Rx_Impl and Observer, respectivley, can be called within generator as well.

If within timeout milliseconds no subscriber has arrived, the stream of events is stopped. This ensures that we get no memory leak.

Source
distinct(rx)

Specs:

The distinct transformation is a filter, which only passes values that it has not seen before. Since all distinct values has to be stores inside the filter, its required memory can grow for ever, if an unbounded sequence is used.

Examples

iex> alias Reaxive.Rx 
iex> [1, 1, 2, 1, 2, 2, 3, 1] |> Rx.generate |> Rx.distinct |> Rx.to_list
[1, 2, 3]
Source
distinct_until_changed(rx)

Specs:

The distinct_until_changed transformation is a filter, which filters out all repeating values, such that only value changes remain in the event sequence.

Examples

iex> alias Reaxive.Rx 
iex> [1, 1, 2, 1, 2, 2, 3, 1] |> Rx.generate |> Rx.distinct_until_changed |> Rx.to_list
[1, 2, 1, 2, 3,  1]
Source
drop(rx, n)

Specs:

drop filters out the first n elements of the sequence.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(10) |> Rx.drop(5) |> Rx.to_list
[5, 6, 7, 8, 9]
Source
drop_while(rx, pred)

Specs:

drop_whilefilters out the first elements while the predicate istrue`.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(10) |> Rx.drop_while(&(&1 < 5)) |> Rx.to_list
[5, 6, 7, 8, 9]
Source
empty(timeout \\ 5000)

Creates the empty sequence of events. After a subscription, the sequence terminates immediately.

Examples

iex> alias Reaxive.Rx
iex> Rx.empty |> Rx.stream |> Enum.to_list
[]
Source
error(exception, timeout \\ 5000)

The error function takes an in Elixir defined exception and generate a stream with the exception as the only element. The stream starts after the first subscription.

Examples

iex> alias Reaxive.Rx
iex> me = self
iex> Rx.error(RuntimeError.exception("yeah")) |> Observable.subscribe(fn(t, x) -> me |> send {t, x} end)
iex> receive do x -> x end
{:on_error, %RuntimeError{message: "yeah"}}
Source
eval(exp)

Evaluates a lazy expression, encoded in Rx.Lazy. Returns the argument if it is not an Rx.Lazy encoded

Source
filter(rx, pred)

Specs:

This function filter the event sequence such that only those events remain in the sequence for which pred returns true.

In Reactive Extensions, this function is called Where.

Examples

iex> alias Reaxive.Rx
iex> require Integer
iex> Rx.naturals |> Rx.take(5) |> Rx.filter(&Integer.is_even/1) |> Rx.to_list
[0, 2, 4]
Source
first(rx)

Specs:

The first element of the event sequence. Does return the first scalar value and dispose the event sequence. The effect is similar to

rx |> Rx.stream |> Stream.take(1) |> Enum.fetch(0)

This function is not lazy, but evaluates eagerly and forces the subscription.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(5) |> Rx.first
0

iex> alias Reaxive.Rx
iex> Rx.return(3) |> Rx.first
3
Source
flat_map(rx, map_fun)

Specs:

The flat_map function takes a mapping function and a sequence of events. It applies the map_fun to each source event. The map_fun is a function which must return a sequence of events (i.e. an Observable). All resulting sequences are then flattened, such that only one sequence of events is returned from flat_map. Since all sequences generated by the mapping process are running concurrently to each other, the flattening process does not ensure any ordering of the resulting event sequence.

In Reactive Extensions, this function is called SelectMany.

Source
generate(collection, delay \\ 0, timeout \\ 5000)

Specs:

The generate function takes a collection and generates for each element of the collection an event. The delay between the events is the second parameter. The delay also takes place before the very first event.

This function is always a root in the net of communicating observables and does not depend on another observable.

This function can also be used with a lazy stream, such that unfolds and the like generate infininte many values. A typical example is the natural number sequence or the tick sequence

naturals = Rx.generate(Stream.unfold(0, fn(n) -> {n, n+1} end))
ticks = Rx.generate(Stream.unfold(:tick, fn(x) -> {x, x} end))

Important Remarks:

  • If the delay is set to too small value (e.g. 0), then the first few elements may be swalloed because no subscriber is available. This might be changed in the future.
Source
map(rx, fun)

Specs:

The map functions takes an observable rx and applies function fun to each of its values.

In ELM, this function is called lift, since it lifts a pure function into a signal, i.e. into an observable.

In Reactive Extensions, this function is called Select.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(5) |> Rx.map(&(2+&1)) |> Rx.stream |> Enum.to_list
[2, 3, 4, 5, 6]
Source
merge(rxs)

Specs:

Source
merge(rx1, rx2)

Specs:

Merges two or more event sequences in a non-deterministic order.

The result sequences finishes after all sequences have finished without errors or immediately after the first error.

Examples

iex> alias Reaxive.Rx
iex> tens=Rx.naturals |> Rx.take(10)
iex> fives=Rx.naturals |> Rx.take(5)
iex> Rx.merge(tens, fives) |> Rx.to_list |> Enum.sort
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9]
Source
naturals(delay \\ 0, timeout \\ 5000)

Specs:

Generates all naturals numbers starting with 0.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(5) |> Rx.stream |> Enum.to_list
[0, 1, 2, 3, 4]
Source
never()

Specs:

The never function creates a sequence of events that never pushes anything.

Source
product(rx)

Specs:

Multiplies all events of the sequence and returns the product as number

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(5) |> Rx.map(&(&1 +1)) |> Rx.product
120

iex> alias Reaxive.Rx
iex> 1..5 |> Rx.generate(1) |> Rx.product
120
Source
reduce(rx, acc, reduce_fun)

Specs:

This function considers the past events to produce new events. Therefore this function is called in ELM foldp, folding over the past.

In Elixir, it is the convention to call the fold function reduce, therefore we stick to this convention.

The result of reduce is an event sequence with exactly one element. To get the scalar value, apply function first to it.

The reduce_fun function is simple, applying the current event together with the accumulator producing a new accumulator. Finally, the accumulator is returned, when the source event stream is finished. The sum reducer could be implemented as

def sum(rx) do
  rx |> Rx.reduce(0, fn(x, acc) -> x + acc end)
end

For more complex reducing functionalities, see the Reaxive.Sync module.

Source
return(value)

Specs:

The return function takes a value and creates an event sequence with exactly this value and terminates afterwards.

It is essentially the same as

generate([value])

Examples:

iex> alias Reaxive.Rx
iex> Rx.return(3) |> Rx.stream |> Enum.to_list
[3]

iex> alias Reaxive.Rx
iex>  Rx.return(5) |> Rx.stream |> Enum.to_list
[5]
Source
start_with(prev_rx, collection)

Specs:

The function start_with takes a stream of events prev_rx and a collection. The resulting stream of events has all elements of colletion, followed by the events of prev_rx.

Examples

iex> alias Reaxive.Rx
iex> Rx.generate([5]) |> Rx.start_with([0, 1, 2]) |> Rx.to_list
[0, 1, 2, 5]
Source
stream(rx)

Specs:

Converts a sequence of events into a (infinite) stream of events.

This operator is not lazy, but eager, as it forces the subscribe and therefore the evaluation of the subscription.

Examples

iex> alias Reaxive.Rx
iex> Rx.generate(1..5) |> Rx.stream |> Enum.to_list
[1, 2, 3, 4, 5]
Source
stream_observer(pid \\ :erlang.self())

Specs:

A simple observer function, sending tag and value as composed message to the process.

Source
sum(rx)

Specs:

Sums up all events of the sequence and returns the sum as number.

Examples

iex> alias Reaxive.Rx iex> 1..5 |> Rx.generate(1) |> Rx.sum 15

iex> alias Reaxive.Rx iex> Rx.naturals |> Rx.map(&(&1 +1)) |> Rx.take(5) |> Rx.sum 15

Source
take(rx, n)

Specs:

This function produces only the first n elements of the event sequence. n must be positive.

A negative n would take elements from the back (the last n elements.) This can be achieved by converting the sequence into a stream and back again:

rx |> Rx.stream |> Stream.take(-n) |> Rx.generate

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(10) |> Rx.to_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Source
take_until(rx, pred)

Specs:

Takes the first elements of the sequence until the predicate is true.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take_until(&(&1 > 5)) |> Rx.to_list
[0, 1, 2, 3, 4, 5]
Source
take_while(rx, pred)

Specs:

Takes the first elements of the sequence while the predicate is true.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take_while(&(&1 < 5)) |> Rx.to_list
[0, 1, 2, 3, 4]
Source
to_list(rx)

Specs:

Converts the event sequence into a regular list. Requires that the sequence is finite, otherwise this call does not finish.

Examples

iex> alias Reaxive.Rx
iex> Rx.naturals |> Rx.take(5) |> Rx.to_list
[0, 1, 2, 3, 4]
Source

Macros

lazy(expr)

This macros suspends an expression and replaces is with an Rx.Lazy thunk.

Source