Skip to content

Instantly share code, notes, and snippets.

@jcreedcmu
Created February 17, 2019 22:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcreedcmu/ce7c0b87a1248e3ead896be7d1c40f7c to your computer and use it in GitHub Desktop.
Save jcreedcmu/ce7c0b87a1248e3ead896be7d1c40f7c to your computer and use it in GitHub Desktop.
pattern.ts
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Elim<T extends { t: string }, U extends string> = T extends { t: U } ? never : T;
type Choose<T extends { t: string }, U extends string> = T extends { t: U } ? T : never;
type Prog<T, U> = { k: 'src', v: T } | { k: 'ans', v: U };
interface PreMatcher<T extends { t: string }> {
cas<S extends string, U>(tag: S, f: (x: Omit<Choose<T, S>, 't'>) => U): Matcher<Elim<T, S>, U>;
}
interface Matcher<T extends { t: string }, U> {
cas<S extends string>(tag: S, f: (x: Omit<Choose<T, S>, 't'>) => U): Matcher<Elim<T, S>, U>;
done(): [T] extends [never] ? U : unknown;
}
class PreMatch<T extends { t: string }> implements PreMatcher<T> {
data: T;
constructor(data: T) {
this.data = data;
}
cas<S extends string, U>(tag: S, f: (x: Omit<Choose<T, S>, 't'>) => U): Match<Elim<T, S>, U> {
if (this.data.t == tag)
return new Match<Elim<T, S>, U>({ k: 'ans', v: f(this.data as Choose<T, S>) })
else
return new Match<Elim<T, S>, U>({ k: 'src', v: this.data as Elim<T, S> });
}
}
class Match<T extends { t: string }, U> implements Matcher<T, U> {
data: Prog<T, U>;
constructor(data: Prog<T, U>) {
this.data = data;
}
cas<S extends string>(tag: S, f: (x: Omit<Choose<T, S>, 't'>) => U): Match<Elim<T, S>, U> {
if (this.data.k == 'src' && this.data.v.t == tag)
return new Match<Elim<T, S>, U>({ k: 'ans', v: f(this.data.v as Choose<T, S>) })
else
return new Match(this.data as Prog<Elim<T, S>, U>);
}
done(): [T] extends [never] ? U : unknown {
return (this as any).data.v as any;
}
}
export function match<T extends { t: string }, U>(data: T): PreMatcher<T> {
return new PreMatch<T>(data);
}
type Foo = { t: 'foo', x: number, z: number } | { t: 'bar', y: string };
function do_a_thing(x: Foo) {
return match(x)
.cas('foo', t => t.x == 3)
.cas('bar', t => t.y == "foo")
.done();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment