Last active
March 21, 2019 10:22
-
-
Save yelouafi/63b2f31eacfd2d51c2d229bdbb1a3d0e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Getter a :: () -> a | |
// gmap :: ((a -> b), Getter a), Getter b | |
function gmap(fn, getter) { | |
return () => fn(getter()) | |
} | |
// gcombine2 :: ((a,b) -> c, Getter a, Getter ab) -> Getter c | |
function gcombine2(fn, getter1, getter2) { | |
return () => fn(getter1(), getter2()) | |
} | |
// gcombine :: ((...a) -> b, ...Getter a) -> Getter b | |
function gcombine(fn, ...getters) { | |
return () => fn(...getters.map(g => g())) | |
} | |
/** | |
Listener a :: a -> () | |
Unsubscribe :: () -> () | |
Notifer a :: Listener a -> Unsubscribe | |
**/ | |
// nmap :: (a -> b, Notifier a) -> Notifier b | |
function nmap(fn, notifier) { | |
return listener => { | |
return notifier(a => listener(fn(a))) | |
} | |
} | |
// merge2 :: (Notifer a, Notifer b) -> Notifer (a | b) | |
function merge2(notify1, notify2) { | |
return listener => { | |
// subscribes the passed listener to both notifers | |
const unsubscribe1 = notify1(listener) | |
const unsubscribe2 = notify2(listener) | |
// will unsubscribe also from both notifiers | |
return () => { | |
unsubscribe1() | |
unsubscribe1() | |
} | |
} | |
} | |
// merge :: (...Notifier a) -> Notifer a | |
function merge(...notifiers) { | |
return listener => { | |
const unsubscribes = notifiers.map(notifier => notifier(listener)) | |
return () => { | |
unsubscribes.forEach(unsubscribe => unsubscribe()) | |
} | |
} | |
} | |
// Never :: Notifier () | |
const Never = () => {} | |
// Source a :: { getter :: Getter a, Notifer:: Notifier () } | |
// simple factory function | |
function Source(getter, notifier) { | |
return { | |
getter, | |
notifier | |
} | |
} | |
// sconst :: a -> Source a | |
function sconst(a) { | |
return Source( | |
() => a, // always return the same data | |
Never // never report changes | |
) | |
} | |
// smap :: ((a -> b), Source a) -> Source b | |
function smap(fn, src) { | |
return Source( | |
gmap(fn, src.getter), // maps over the Getter with the provided function | |
src.notifier // reuses the Notifier of the input Source | |
) | |
} | |
// scombine2 :: ((a, b) -> c, Source a, Source b) -> Source c | |
function scombine2(fn, src1, src2) { | |
return Source( | |
gcombine2(fn, src1.getter, src2.getter), // compose using Applicatives | |
merge2(src1.notifier, src2.notifier) // compose using Monoids | |
) | |
} | |
// scombine :: ((...a) -> b, ...Source a) -> Source b | |
function scombine(fn, ...srcs) { | |
return Source( | |
gcombine(fn, ...srcs.map(src => src.getter)), | |
merge(...srcs.map(src => src.notifier)) | |
) | |
} | |
// Action a :: a -> () | |
// react :: (Source a, Action a) -> Unsubscribe | |
function react(src, action) { | |
const listener = () => action(src.getter()) | |
// executes the action as initialization | |
listener() | |
// returns a function to unsubscribe the action | |
return src.notifier(listener) | |
} | |
// Simple event emitter | |
function Emitter() { | |
let listeners = new Set() | |
return { | |
subscribe(listener) { | |
listeners = listeners.add(listener) | |
return () => { | |
listeners = listener.delete(listener) | |
} | |
}, | |
emit(data) { | |
listeners.forEach(listener => listener()) | |
} | |
} | |
} | |
// Source for mutabe variables | |
function Mutable(seed) { | |
const emitter = Emitter() | |
const src = Source( | |
() => seed, | |
emitter.subscribe | |
) | |
// provide a method to mutate the state manually | |
src.set = newValue => { | |
seed = newValue | |
emitter.emit() | |
} | |
return src | |
} | |
// Source for reactive Behaviours | |
function Behaviour(seed, ...cases) { | |
cases.forEach(([notifier, reducer]) => { | |
notifier(event => { | |
seed = reducer(seed, event) | |
}) | |
}) | |
return Source( | |
() => seed, | |
merge(...cases.map(([notifier, _]) => notifier)) | |
) | |
} | |
function domNotifier(target, event) { | |
return (listener) => { | |
target.addEventListener(event, listener) | |
return () => { | |
target.removeEventListener(event, listener) | |
} | |
} | |
} | |
function inputValueSource(element, event) { | |
return Source( | |
() => element.value, | |
domNotifier(element, event) | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment