Created
April 3, 2018 13:05
-
-
Save ivenmarquardt/681bfebda6e7f5d0825aaaa6661a46b8 to your computer and use it in GitHub Desktop.
Functional reactive programming types: Event(-Stream) / Behavior
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
// data constructor | |
const Data = name => Dcons => { | |
const Data = k => { | |
const t = new Tcons(); | |
t[`run${name}`] = k; | |
t.tag = name; | |
return t; | |
}; | |
const Tcons = Function(`return function ${name}() {}`) (); | |
return Dcons(Data); | |
}; | |
// reactive types | |
const Event = Data("Event") (Event => k => Event(k)); | |
const Behavior = Data("Behavior") (Behavior => k => Behavior(k)); | |
// helper | |
const subscribe = o => { | |
o.target.addEventListener( | |
o.type, | |
o.listener, | |
o.options | |
); | |
return () => o.target.removeEventListener( | |
o.type, | |
o.listener, | |
o.options | |
); | |
}; | |
// Behavior instance | |
const ButtonPressed = initialState => { | |
let state = initialState; | |
const cancelDown = subscribe({ | |
target: document, | |
type: "mousedown", | |
listener: _ => state = true, | |
options: {capture: true} | |
}); | |
const cancelUp = subscribe({ | |
target: document, | |
type: "mouseup", | |
listener: _ => state = false, | |
options: {capture: true} | |
}); | |
return Object.assign( | |
Behavior((k, e) => k(state)), | |
{cancel: () => (cancelDown(), cancelUp())} | |
); | |
}; | |
// Event instance | |
const mouseCoords = Event((k, e) => subscribe({ | |
target: document, | |
type: "mousemove", | |
listener: event => buttonPressed.runBehavior(b => b ? k({x: event.screenX, y: event.screenY}) : null), | |
options: {capture: true} | |
})); | |
// Event combinators | |
const comp = f => g => x => f(g(x)); | |
const map = f => tk => | |
Event((k, e) => tk.runEvent(x => k(f(x)), e)); | |
const filter = p => tk => | |
Event((k, e) => tk.runEvent(x => p(x) ? k(x) : null, e)); | |
// build a lazy evaluated action | |
// stream even mouse coords | |
// in the format "#x#" | |
// but only if the right button is pressed | |
const action = comp(map(coords => | |
`${coords.x}x${coords.y}`)) | |
(filter(coords => | |
(coords.x & 1) === 0 && (coords.y & 1) === 0)) | |
(mouseCoords); | |
// register the Behavior with false as initial value | |
const buttonPressed = ButtonPressed(false); | |
// register the Event Stream | |
const cancel = action.runEvent(console.log); | |
console.log("1. Copy to the browser console (chromium tested only)"); | |
console.log("2. Position cursor within document"); | |
console.log("3. Press a mouse button and move the cursor..."); | |
console.log("4. Is canceled after 10 secs"); | |
setTimeout(() => (cancel(), buttonPressed.cancel()), 10000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment