LoginSignup
1
0

More than 3 years have passed since last update.

Typescriptでパターンマッチもどき

Last updated at Posted at 2019-08-05

下記のスライド読んでて、enum的なもの以外でもできるのかなと思って少し考えてみた。

もう少しボイラープレート減らせないのかなぁ、とは思うがTypeScriptよく知らないので、私にはこれ以上どうにもならなかった。

なお、TypeScript playgroundでコンパイルエラーがないか確認しただけなので、ちゃんと動くかはわかりません(爆)。

あと、判別共用体やるなら種別を表す共通のプロパティを作ってswitch文で分岐するのが正解っぽい。

type CaseOf = { [K in string]: any[] }

type CaseOfMethod<C extends CaseOf> = 
    <R>(c: { [K in keyof C]: (...x: C[K]) => R }) => R

// data Maybe a = Nothing | Just a
type CaseOfMaybe<A> = {Nothing:[], Just:[A]}

class Maybe<A> {
    private constructor(readonly caseOf: CaseOfMethod<CaseOfMaybe<A>>){}

    static readonly Nothing = <A>(...x: CaseOfMaybe<A>["Nothing"]) =>
        new Maybe<A>(c => c.Nothing(...x))

    static readonly Just = <A>(...x: CaseOfMaybe<A>["Just"]) =>
        new Maybe<A>(c => c.Just(...x))

    readonly map = <B>(f: (x: A) => B): Maybe<B> => this.caseOf({
        Nothing:() => Maybe.Nothing(),
        Just:(x) => Maybe.Just(f(x))
    })
}

// data List a = Nil | Cons a (List a)
type CaseOfList<A> = {Nil:[], Cons:[A, List<A>]}

class List<A> {
    private constructor(readonly caseOf: CaseOfMethod<CaseOfList<A>>){}

    static readonly Nil = <A>(...x: CaseOfList<A>["Nil"]) =>
        new List<A>(c => c.Nil(...x))

    static readonly Cons = <A>(...x: CaseOfList<A>["Cons"]) =>
        new List<A>(c => c.Cons(...x))

    readonly map = <B>(f: (x: A) => B): List<B> => this.caseOf({
        Nil:() => List.Nil(),
        Cons:(x, xs) => List.Cons(f(x), xs.map(f))
    })
}

// data Bool = False | True
type CaseOfBool = {False:[], True:[]}

class Bool {
    private constructor(readonly caseOf: CaseOfMethod<CaseOfBool>){}

    static readonly False = (...x: CaseOfBool["False"]) =>
        new Bool(c => c.False(...x))

    static readonly True = (...x: CaseOfBool["True"]) =>
        new Bool(c => c.True(...x))

    readonly and = (other: Bool): Bool => this.caseOf({
        False:() => this,
        True:() => other
    })

    readonly or = (other: Bool): Bool => this.caseOf({
        False:() => other,
        True:() => this
    })

    readonly not = ():Bool => this.caseOf({
        False:() => Bool.True(),
        True:() => Bool.False()
    })
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0