Skip to content

Instantly share code, notes, and snippets.

@bradparker
Created June 16, 2019 05:33
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 bradparker/cb747056241917da6f72ccc9fcbad507 to your computer and use it in GitHub Desktop.
Save bradparker/cb747056241917da6f72ccc9fcbad507 to your computer and use it in GitHub Desktop.
Geneator function do notation experiment
import * as Either from "./Either";
type Action<I, E, A> = (input: I) => Promise<Either.Either<E, A>>;
export class App<I, E, A> {
public run: Action<I, E, A>;
public constructor(action: Action<I, E, A>) {
this.run = action;
}
public map<B>(f: (a: A) => B): App<I, E, B> {
return new App(input => {
return this.run(input).then(result => result.map(f));
});
}
public chain<B>(f: (a: A) => App<I, E, B>): App<I, E, B> {
return new App(input => {
return this.run(input).then(result => {
return result.run(
l => {
return Promise.resolve(new Either.Left(l));
},
r => {
return f(r).run(input);
}
);
});
});
}
public handleError<B>(f: (e: E) => App<I, E, A>): App<I, E, A> {
return new App(input => {
return this.run(input).then(result => {
return result.run(
l => {
return f(l).run(input);
},
r => {
return Promise.resolve(new Either.Right(r));
}
);
});
});
}
}
export const throwError = <I, E, A>(e: E): App<I, E, A> =>
new App(() => Promise.resolve(new Either.Left(e)));
export const pure = <I, E, A>(a: A): App<I, E, A> =>
new App(() => Promise.resolve(Either.pure(a)));
export const ask = <I, E, A>(): App<I, E, I> =>
new App(input => Promise.resolve(Either.pure(input)));
export const appBlock = <I, E, A>(
block: () => Iterator<App<I, E, A>>
): App<I, E, A> => {
const iterator = block();
const step = (value?: A) => {
const result = iterator.next(value);
if (result.done) {
return result.value;
}
return result.value.chain(step);
};
return step();
};
const example1 = () =>
appBlock(function*() {
const i = yield ask();
const a = yield pure(1);
const b = yield pure("Foo");
const c = yield throwError("This is no good").handleError(pure);
return pure(`${i} ${a.toString()} ${b} ${c.toString()}`);
});
example1()
.run("Our env")
.then(console.log);
const example2 = () =>
appBlock(function*() {
const a = yield example1();
return pure(a);
});
example2()
.run("I have no idea what this'll do")
.then(console.log);
ask()
.chain(input => pure(`We recieved: ${input}`))
.run("Another example")
.then(console.log);
export type Either<E, A> = Left<E, A> | Right<E, A>;
export class Left<E, A> {
public readonly tag: "Left" = "Left";
public readonly value: E;
public constructor(value: E) {
this.value = value;
}
public map<B>(_f: (a: A) => B): Either<E, B> {
return new Left(this.value);
}
public chain<B>(_f: (a: A) => Either<E, B>): Either<E, B> {
return new Left(this.value);
}
public run<R>(l: (e: E) => R, _r: (a: A) => R): R {
return l(this.value);
}
}
export class Right<E, A> {
public readonly tag: "Right" = "Right";
public readonly value: A;
public constructor(value: A) {
this.value = value;
}
public map<B>(f: (a: A) => B): Either<E, B> {
return new Right(f(this.value));
}
public chain<B>(f: (a: A) => Either<E, B>): Either<E, B> {
return f(this.value);
}
public run<R>(l: (e: E) => R, r: (a: A) => R): R {
return r(this.value);
}
}
export const pure = <E, A>(a: A): Either<E, A> => {
return new Right(a);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment