Created
February 17, 2019 22:50
-
-
Save jcreedcmu/ce7c0b87a1248e3ead896be7d1c40f7c to your computer and use it in GitHub Desktop.
pattern.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
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