Last active
March 17, 2019 23:55
-
-
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
This file contains 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
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) |
This file contains 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
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