The Observable disguised as an IO Monad

  1. A unit function used to place (lift) a value into the monad container. You could also find it as from() or return().
  2. A mapping function that lifts regular functions that work over these values, returning back a new instance of (generally) the same type containing the newly computed value.
  3. A flatMap() also known as (bind()) that lifts a monad-returning function to work over the value in the container and flatten its result into a single structure.
interface Monad {
of(a): Monad<a>
map(a -> b): Monad<b>
flatMap(a -> M<b>): Monad<b>
}

The IO Monad

const rotate = function(document, id) {
return function(deg) {
let elem = document.querySelector(`${id}`);
elem.style[‘transform’] = `rotate(${deg}deg)`;
};
};
const rotateDom = _.partial(rotate, document);
class IO {   constructor(effect) {
if (!_.isFunction(effect)) {
throw ‘IO Usage: function required’;
}
this.effect = effect;
}
// unit function
static of(a) {
return new IO( () => a );
}
// map function
map(fn) {
let self = this;
return new IO(() => fn(self.effect()));
}
// monadic bind
flatmap(fn) {
let self = this
return new IO(() => fn(self.effect()).run());
}

run() {
return this.effect();
}
}
IO.of(rotateDom('#name')).map(doNIntervals(12, 100)).run();
function doNIntervals(n, interval) { 
return function times(fn) {
setTimeout(() => {
fn(n * -30);
if( — n >= 0) {
times(fn);
}
}, interval);
};
}

The Observable

Rx.Observable.interval(100)
.map(val => val * 30 + 30)
.take(12)
.subscribe(rotateDom('#name2'));

Comparing IO and Observable

  1. Both monads are lazy in that the Observable won’t begin executing until subscribe() is called, and similarly IO won’t start until run() is called.
  2. IO’s run() is what causes the chain of side effects to propagate and flush out any pending IO operations. Likewise, subscribe() receives any events propagated from the streams that it’s made up of and performs any necessary IO operations, rotating the DIV in this case. In a pure FRP observable chain, all of the side effects should be propagated downstream to the subscribers.
newtype Observable a = S (IO a) deriving (Functor , Applicative, Monad)

--

--

Author of Functional Programming in JavaScript, RxJS in Action, and The Joy of JavaScript

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store