Otters - Cats in Streams
Otters uses the Cats library to provide a few typeclasess and useful syntax for working with streaming libraries.
Out of the box Otters supports
All streaming libraries share common concepts, Otters attempts to create typeclasses and corresponding laws based on Typelevel Cats so that common patterns may be shared between all implementations, without masking their differences.
Streams provide a convienent way of expressing coinductive problems, meaning the input may never end, e.g. a web server.
A Stream is essentially models an infinite collection of units of asynchronous work. Streams may be piped through other streams and sent to sinks.
A stream can be represented in the abstract as F[A]
where F
is the type specific to the library (see below) and A
is the value type contained within that stream. E.g Observable[Int]
in Monix.
A pipe can be represented F[A] => F[B]
where F
is a stream. This allows multiple streams to be composed.
A sink can be represented F[A] => G[B]
where F
is a stream and G
is the materialized value (see below).
Library | Stream Type | Asyncronous Type | Materialized Type |
---|---|---|---|
Akka Streams | Source |
Future |
RunnableGraph |
Monix Reactive | Observable |
Task |
Task |
Monix Tail | Iterant |
Any Effect * |
Any Effect * |
FS2 | Stream |
Any Effect * |
Any Effect * |
*Effect
refers to Cats Effect, where IO
is the reference implementation, but this could be anything, for example Monix's Task
.
Notice that only Akka Streams has a different materialized type. This is because Future
is eagerly evaluated, meaning that the moment it is assigned to a value it will start doing its work. Eager evaluation is at odds with reactive streaming concepts, where the you can construct a graph from many logical parts and as a separate activity execute that graph as a fully connected stream. The type RunnableGraph
represents lazy evaluation of the stream, only when .run()
is called does the execution begin.
Monix and FS2 are different in this respect as Task
and Effect
are lazily evaluated and therefore the materialized type can be the same as the asyncronous type used for each unit of work within the stream.
For example the result of sending a source to a sink which collects the result in a Seq
in Akka Streams will be RunnableGraph[Future[Seq[A]]]
, however because both the materialized and asychronous types in Monix and Fs2 are the same the effects can be captured in a single type, e.g. Task[Seq[A]]
or IO[Seq[A]]
.
The writer monad from Cats can be used in a streaming context to capture some state to be committed along with the main data at the end of the stream. This could be something like a Kafka offset or some stats regarding the execution.
[more to follow]
The either monad allows data to be routed via different pipes and to different sinks.
[more to follow]
This project supports the Typelevel code of conduct and aims that its channels (mailing list, Gitter, github, etc.) to be welcoming environments for everyone.