Skip to content

Instantly share code, notes, and snippets.

@bkrmendy
Created September 18, 2021 18:43
Show Gist options
  • Save bkrmendy/059df1e6891126cf562f2b8a07d34fb7 to your computer and use it in GitHub Desktop.
Save bkrmendy/059df1e6891126cf562f2b8a07d34fb7 to your computer and use it in GitHub Desktop.
Typescript effects via generators
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