Skip to content

Instantly share code, notes, and snippets.

@ivenmarquardt
Created April 3, 2018 13:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ivenmarquardt/681bfebda6e7f5d0825aaaa6661a46b8 to your computer and use it in GitHub Desktop.
Save ivenmarquardt/681bfebda6e7f5d0825aaaa6661a46b8 to your computer and use it in GitHub Desktop.
Functional reactive programming types: Event(-Stream) / Behavior
// 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