LoginSignup
1
0

More than 5 years have passed since last update.

sealedトレイトを階層化してパターンマッチ

Last updated at Posted at 2018-06-15

動機

ある対象target: Tに対してバリデーションを行い、

  • バリデーションをパスしなかった場合
    • バリデーションをパスしなかった理由でハンドリングしたい
  • バリデーションをパスした場合
    • バリデーションをパスした場合のtarget: Tの状態でハンドリングしたい

みたいな動機があって、
def validate(target: T): Either[UnPassed, Passed]
のようなバリデーションの為の関数を実装した際に、返り値全体を同じグループとして表現したかった。

実例

以下の様にsealed traitを継承し、階層化します。階層化はあまり珍しくないかと思います。


sealed trait Result
object Result {

    sealed trait Passed extends Result
    object Passed {

        case object Status1 extends Passed
        case object Status2 extends Passed
        ...
        case object StatusN extends Passed

    }

    sealed trait UnPassed extends Result
    object UnPassed {

        case object Reason1 extends UnPassed
        case object Reason2 extends UnPassed
        ...
        case object ReasonN extends UnPassed

    }

}

sealed trait以外にも、Scalaの列挙型なら継承によって階層化することができます。
例えば、以下の様にfinal case classを用いると、特定の理由の時には特定の値を返す様なことができます。

final case class Reason(value: V) extends UnPassed

意味のまとまりが生まれるのが階層化を行う一番のメリットだと思います。

パターンマッチを行う

階層化した列挙型は、階層的なsealedの恩恵を得ることができます。
マッチ対象の型を一番上のReasonとすれば、match式のパターンマッチではReasonを継承する全ての要素をマッチしなければなりません。その下のPassedまたはUnPassedをマッチ対象にすると、それぞれを継承する全ての要素をマッチする必要があります。

例えば、マッチ対象をResult.Passedにすれば、バリデーションを通過した全ての状態をハンドリングすることができます。

val handler: Result.Passed => V = (status: Result.Passed) =>
    status match {
        case Result.Passed.Status1 => ???
        case Result.Passed.Status2 => ???
        ...
        case Result.Passed.StatusN => ???
    }

仮に、Result.Passed.Status1のケースを登録しない様なPartialFunctionを作成しようとすると、

warning: match may not be exhaustive.
It would fail on the following input: Status1

の様に警告を出してくれます。

Eitherとの組み合わせ

やり方は色々あると思いますが、実際にEitherと組み合わせて使う場合には以下の様になります。

def validate(target: T): Either[UnPassed, Passed]

def statusHandler(status: Passed): V
def reasonHandler(reason: UnPassed): V

validate(target) match {
    case Right(status) => statusHandler(status)
    case Left(reason) => reasonHandler(reason)
}

もっと良い方法もあるかと思いますので、何かあればおしらせください。

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