automaton: Effectful streams and automata in coalgebraic encoding

[ library, mit, program, streaming ] [ Propose Tags ] [ Report a vulnerability ]

Effectful streams have an internal state and a step function. Varying the effect type, this gives many different useful concepts: For example with a reader effect, it results in automata (a.k.a transducers or state machines).


[Skip to Readme]

Flags

Manual Flags

NameDescriptionDefault
dev

Enable warnings as errors. Active on ci.

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 1.3, 1.4, 1.5
Change log CHANGELOG.md
Dependencies automaton, base (>=4.16 && <4.21), mmorph (>=1.2 && <1.3), MonadRandom (>=0.5), mtl (>=2.2 && <2.4), profunctors (>=5.6 && <5.7), selective (>=0.7 && <0.8), semialign (>=1.2 && <=1.4), simple-affine-space (>=0.2 && <0.3), these (>=1.1 && <=1.3), transformers (>=0.5) [details]
License MIT
Author Manuel Bärenz
Maintainer programming@manuelbaerenz.de
Category Streaming
Source repo head: git clone https://github.com/turion/rhine.git
this: git clone https://github.com/turion/rhine.git(tag v1.5)
Uploaded by turion at 2024-11-12T19:29:27Z
Distributions NixOS:1.4, Stackage:1.5
Reverse Dependencies 3 direct, 1 indirect [details]
Executables ForeverSawtooth, UserSawtooth
Downloads 154 total (38 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2024-11-12 [all 1 reports]

Readme for automaton-1.5

[back to package description]

automaton: Effectful streams and automata as coalgebras

This library defines effectful streams and automata, in coalgebraic encoding. They are useful to define effectful automata, or state machines, transducers, monadic stream functions and similar streaming abstractions. In comparison to most other libraries, they are implemented here with explicit state types, and thus are amenable to GHC optimizations, often resulting in dramatically better performance.

What?

The core concept is an effectful stream in coalgebraic encoding:

data StreamT m a = forall s.
  StreamT
  { state :: s
  , step :: s -> m (s, a)
  }

This is a stream because you can repeatedly call step on the state and produce output values a, while mutating the internal state. It is effectful because each step performs a side effect in m, typically a monad.

The definition you will most often find in the wild is a direct fixpoint, or recursive datatype:

data StreamT m a = StreamT (m (StreamT m a, a))

Semantically, there is no big difference between them, and in nearly all cases you can map the coalgebraic encoding onto the recursive one and vice versa, by means of the final coalgebra. (For the few edge cases, see the section in Data.Automaton about recursive definitions.) But when composing streams, the coalgebraic encoding will usually be more performant that than the recursive one because GHC can optimise the joint state and step functions of the streams.

How are these automata?

Effectful streams are very versatile, because you can change the effect type m to get a number of different concepts. When m contains a Reader effect, you get automata! From the effectful stream alone, a side effect, a state transition and an output value is produced at every step. If this effect includes reading an input value, you have all ingredients for an automaton (also known as a Mealy state machine, or a transducer).

Automata can be composed in many useful ways, and are very expressive. A lot of reactive programs can be written with them, by composing a big program out of many automaton components.

Why?

Mostly, performance. When composing a big automaton out of small ones, the recursive definition is not very performant, as mentioned above: Each step of each component contains a closure, which is basically opaque for the compiler. In the coalgebraic encoding, the step functions of two composed automata are themselves composed, and the compiler can optimize them just like any regular function. This often results in massive speedups.

But really, why?

To serve as the basic building block in rhine, a library for Functional Reactive Programming.

Doesn't this exist already?

Not quite. There are many streaming libraries (streamt, streaming), and state machine libraries (machines) that implement effectful streams. Prominently, dunai implements monadic stream functions (which are essentially effectful state machines) and has inspired the design and API of this package to a great extent. (Feel free to extend this list by other notable libraries.) But all of these are implemented recursively.

I am aware of only two fleshed-out implementations of effectful automata in the coalgebraic encoding, both of which have been a big inspiration for this package: