Description
The module Data.MonadicStreamFunction.Async
only contains a single function, concatS
, which uses the MSF
constructor (which is discouraged). I've often thought that this function must be a special case of some more general principle, similar to how Yampa's switch
functions are special cases of exception handling. The module is called Async
because it transforms MSF
s such that they consume a different number of input and output samples, as opposed to synchronous transformations like morphS
which preserve this.
I got the basic insight for a principled generalization from @AshleyYakeley in haskell/mtl#85 (comment). There, we were discussing how ListT
interacts with other monads, and that it is a kind of a stream transformer. The argument is: since MSF (MaybeT m) a b == ListT (ReaderT a m) b
, it should have a monad instance. This means that if we define
newtype MSFAsync m a b = MSFAsync (MSF (MaybeT m) a b)
we can define Monad m => Monad (MSFAsync m a)
.
This monad instance can be interpreted this way:
- An
MSFAsync m a b
may output several (potentially infinitely many)b
s, while producing side effects inm
and consuming onea
for everyb
. It can be understood as a side-effectful,a
-consuming list ofb
s. return
produces a 1-element list without any further side effects, disregarding the input value.>>=
flatmaps the lists, and similarlyjoin
concatenates them.
In particular, if we have msfasync >>= f
, and msfasync
never terminates (producing infinitely many samples), the result will also never terminate. This special case is concatS
.