Created
September 18, 2021 18:43
-
-
Save bkrmendy/059df1e6891126cf562f2b8a07d34fb7 to your computer and use it in GitHub Desktop.
Typescript effects via generators
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
function maybe<T>(gen: () => Generator<T, any, any>): T | null { | |
const next = gen(); | |
let last = undefined; | |
while (true) { | |
const { value, done } = next.next(last); | |
if (done) { | |
return value; | |
} | |
if (value == null) { | |
return null; | |
} | |
last = value; | |
} | |
} | |
interface AskF { type: "ask" } | |
function Ask(): AskF { | |
return { type: "ask" }; | |
} | |
function reader<T, U>(readerV: T, gen: () => Generator<AskF, any, any>) { | |
const next = gen(); | |
while (true) { | |
const { value, done } = next.next(readerV); | |
if (done) { | |
return value; | |
} | |
} | |
} | |
const maybeAdd = (a: number | null, b: number | null) => maybe(function*() { | |
const aa = yield a; | |
const bb = yield b; | |
return aa + bb; | |
}); | |
interface TellF<T> { type: "tell", thing: T }; | |
function tell<T>(message: T): TellF<T> { | |
return { type: "tell", thing: message }; | |
} | |
function writer<T>(write: (_: T) => void, gen: () => Generator<TellF<T>, any, any>) { | |
const next = gen(); | |
while (true) { | |
const { value, done } = next.next(); | |
if (done) { | |
return value; | |
} | |
write(value.thing); | |
} | |
} | |
console.log(maybeAdd(null, 4)); | |
const withReader = () => reader(5, function*() { | |
const stateA = yield Ask(); | |
const separator = " is still "; | |
const stateB = yield Ask(); | |
return stateA + separator + stateB; | |
}); | |
console.log(withReader()); | |
const logs: string[] = []; | |
const log = (message: string) => logs.push(message); | |
const withWriter = () => writer(log, function*() { | |
yield tell("Starting engine"); | |
yield tell("Liftoff!"); | |
const fuel = 55; | |
const oxygen = 66; | |
yield tell(`Stats: fuel: ${fuel}, oxygen: ${oxygen}`); | |
}); | |
withWriter(); | |
console.log(logs); | |
interface PutF<T> { type: "put", put: T }; | |
const put = <T>(put: T): PutF<T> => ({ type: "put", put }); | |
interface GetF { type: "get" }; | |
const get = (): GetF => ({ type: "get" }); | |
interface ModifyF<T> { type: "modify", modify: (_: T) => T }; | |
const modify = <T>(modify: (_: T) => T): ModifyF<T> => ({ type: "modify", modify }); | |
type StateF<T> = PutF<T> | GetF | ModifyF<T>; | |
function state<T>(state: T, run: () => Generator<StateF<T>, any, any>) { | |
const next = run(); | |
while (true) { | |
const { value, done } = next.next(state); | |
if (done === true) { | |
return value; | |
} | |
if (value.type === "put") { | |
state = value.put; | |
} else if (value.type === "get") { | |
// nothing | |
} else if (value.type === "modify") { | |
state = value.modify(state); | |
} | |
} | |
} | |
const withState = () => state(3, function*() { | |
const state1 = yield get(); | |
console.log("state1 = ", state1); | |
yield put(6); | |
const state2 = yield get(); | |
console.log("state2 = ", state2); | |
yield modify(a => a + 4); | |
const state3 = yield get(); | |
console.log("state3 = ", state3); | |
}); | |
withState(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment