Skip to content

Instantly share code, notes, and snippets.

@briancavalier
Last active March 17, 2019 23:55
Show Gist options
  • Save briancavalier/e681a4c20f83e3d60617b4fd528e203f to your computer and use it in GitHub Desktop.
Save briancavalier/e681a4c20f83e3d60617b4fd528e203f to your computer and use it in GitHub Desktop.
Small encoding of algebraic effects in Typescript (probably works in Flow, too, with syntax tweaks) used in helicopter: https://github.com/briancavalier/helicopter/blob/master/packages/core/src/fx.ts
export type Cancel = void | ((k: (r: void) => void) => void)
export const runCancel = (c: Cancel, k: (r: void) => void): void =>
c ? c(k) : k()
export type Fx<H, A> = (handler: H, k: (a: A) => void) => Cancel
export type Pure<A> = Fx<{}, A>
export const handle = <H0, H1, A>(fx: Fx<H0 & H1, A>, h1: H1): Fx<H0, A> =>
(h0, k) => fx({ ...h0, ...h1 } as H0 & H1, k)
export const runPure = <A>(fx: Fx<{}, A>, k: (a: A) => void = () => {}): Cancel =>
fx({}, k)
export const pure = <A> (a: A): Pure<A> =>
(_, k) => k(a)
export const map = <H, A, B> (f: (a: A) => B, fx: Fx<H, A>): Fx<H, B> =>
(h, k) => fx(h, a => k(f(a)))
export const mapTo = <H, A, B> (b: B, fx: Fx<H, A>): Fx<H, B> =>
map(_ => b, fx)
export const chain = <HA, HB, A, B>(f: (a: A) => Fx<HA, B>, fx: Fx<HB, A>): Fx<HA & HB, B> =>
(env, k) => {
let cancel = fx(env, a => {
cancel = f(a)(env, k)
})
return k => runCancel(cancel, k)
}
export const forever = <H, A>(fx: Fx<H, A>): Fx<H, never> =>
chain(() => forever(fx), fx)
import { Cancel, chain, forever, Fx, handle, map, runPure } from './fx'
import { EOL } from 'os'
import { createInterface } from 'readline'
// Print effect and constructor
type Print = {
print(s: string, k: (r: void) => void): Cancel
}
const print = (s: string): Fx<Print, void> =>
({ print }, k) => print(s, k)
// Read effect and constructor
type Read = {
read(k: (r: string) => void): Cancel
}
const read: Fx<Read, string> =
({ read }, k) => read(k)
// Helper to append newlines
const addEOL = (s: string): string => `${s}${EOL}`
// Effectful computation that prints a prompt, reads
// user input and prints it.
const echo: Fx<Print & Read, void> =
chain(() => chain(print, map(addEOL, read)), print('> '))
// To run echo, we need to provide handlers for the
// Read and Print effect
// We'll use node's readline to implement a Read handler
const rl = createInterface({
input: process.stdin,
output: process.stdout
})
// Implement the Print and Read effect handlers
const handlers: Print & Read = {
print: (s, k): Cancel => k(void process.stdout.write(s)),
read: k => {
rl.once('line', k)
return () => rl.removeListener('line', k)
}
}
// Loop echo forever using the effect handlers
runPure(handle(forever(echo), handlers))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment