kefirjs / kefir

A Reactive Programming library for JavaScript
https://kefirjs.github.io/kefir/
MIT License
1.88k stars 97 forks source link

Fantasy Land compatibility #160

Open rpominov opened 9 years ago

rpominov commented 9 years ago

Creating this issue to investigate how we could make Kefir compatible with the Fantasy Land Specification.

I think of two possibilities: add Fantasy Land methods directly into Kefir, or create a plugin as a separate npm package that injects Fantasy Land methods into Kefir. Don't know what is better for us yet, and would like to know your opinions.

In either way first we need to find out how Fantasy Land methods map to current Kefir methods. Here is a draft of such mapping (not sure if it's correct and complete; will update it in progress):

Setoid

a.equals(a) – we can't implement this (?)

Semigroup

a.concat(b)obsA.concat(obsB) (?)

Monoid

m.empty()Kefir.never() (?)

Functor

u.map(f)obs.map(f)

Apply

a.ap(b) — can be implemented on top of combine, although would be better for us (for performance) if FL would require liftN instead of ap

Applicative

a.of(x)Kefir.constant(x) (?)

Foldable

u.reduce(f, x)Kefir.scan(f, x).last() (?)

One problem is that we can't return the result value directly but can only return an observable containing result value. Although FL seem to not require that.

Traversable

u.sequence(of) — not sure...

Here we have the same problem as in reduce — unable to return result value directly:

// we can't do this
Observavble (f a) -> f (Observable a)

// only can do this (based on reduce)
Observavble (f a) -> Observable (f (Observable a))

Observable (f (Observable a)) already doesn't seem to be very useful, but the main problem is that we can't satisfy the following law:

t(u.sequence(f.of)) is equivalent to u.map(t).sequence(g.of) where t is a natural transformation from f to g (naturality)

Although the other way around form of sequencef (Observable a) -> Observavble (f a) seems very similar to Kefir.combine :: [Observable] -> Observable []. Not sure what it gives us though :)

For better understanding here is how sequence for Array looks like:

Array.prototype.sequence = function(of) {
  return this.reduce((r, i) => r.map(xs => x => xs.concat(x)).ap(of(i)), of([]))
}

Chain / Monad

m.chain(f)obs.flatMap(f) (?)

Extend

w.extend(f) — ?

Comonad

w.extract() — can't be implemented because of asynchronous nature of observables (?)

rpominov commented 8 years ago

We still don't have Fantasy Land compatibility, but I've just added Static Land support http://rpominov.github.io/kefir/#static-land

rpominov commented 8 years ago

Here is how Kefir methods map to FL/SL methods btw: https://github.com/rpominov/kefir/blob/29f0cdc472c3f9110fb6563bd5567ecf5d043243/src/interop/static-land.js (missing methods are impossible to implement for Observable)