Skip to content

Instantly share code, notes, and snippets.

@yelouafi
Last active March 21, 2019 10:22
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yelouafi/63b2f31eacfd2d51c2d229bdbb1a3d0e to your computer and use it in GitHub Desktop.
Save yelouafi/63b2f31eacfd2d51c2d229bdbb1a3d0e to your computer and use it in GitHub Desktop.
// 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