Either、Option、Tryをソースレベルで理解

5137 ワード

差異
  • Either

  • 結果の2つの可能性を表し、1つはRight、1つはLeft
  • Option

  • 選択可能な値を表し、1つはSome(値を表す)、1つはNone(値が空).結果がnullの場合によく用いられる.
  • Try

  • 演算の結果、2つのケースがあり、1つは正常に動作している、すなわちSuccess、1つは運転エラー、異常放出、すなわちFailureであり、そのうちFailureには異常の情報が含まれている.
    共通点
    3つとも2つの可能性の値がある.いずれも結果の上でmapflatMapなどの操作が可能である.
  • Either
  • RightLeftは、Eitherから継承された2つcaseクラスである.
        //Left
        final case class Left[+A, +B](@deprecatedName('a, "2.12.0") value: A) extends Either[A, B]
        
        //Right
        final case class Right[+A, +B](@deprecatedName('b, "2.12.0") value: B) extends Either[A, B]
    
    Eihter1つの結果の2つの可能性を表し、1つはRight、1つはLeft;
        import scala.io.StdIn._
        val in = readLine("Type Either a string or an Int: ")
        val result: Either[String,Int] =
          try Right(in.toInt)
          catch {
            case e: NumberFormatException => Left(in)
          }
        result match {
          case Right(x) => s"You passed me the Int: $x, which I will increment. $x + 1 = ${x+1}"
          case Left(x)  => s"You passed me the String: $x"
        }
    
    Either偏っているRight値の場合、Either使用mapflatMap等の操作の場合、Eitherの結果Rightの場合のみ、操作がトリガーされる.習慣的にLeft値は悪い結果(失敗した結果)、Right良い結果(成功した結果)を表す.
        def doubled(i: Int) = i * 2
        Right(42).map(doubled) // Right(84)
        Left(42).map(doubled)  // Left(42)
    
    Either定義したflatMapmapのため、Eitherに対して使用可能for comprehensions;
        val right1 = Right(1)   : Right[Double, Int] //  right1   
        val right2 = Right(2)
        val right3 = Right(3)
        val left23 = Left(23.0) : Left[Double, Int]  //  left23   
        val left42 = Left(42.0)
        for {
          x 

    ただし、ガード式の使用はサポートされていません.
        for {
          i  0
        } yield i
        // error: value withFilter is not a member of Right[Double,Int]
    

    同様に、以下もサポートされていません
        for (x: Int 
    for comprehensions使用mapおよびflatMapのため、パラメータのタイプを導出しなければならず、このタイプはEitherでなければならない.特に、Either偏向Rightのため、Eitherに対する値はLeftそのタイプを指定しなければならないが、そうでなければ、その位置のデフォルトタイプはNothingである.
        for {
          x 
  • Option
  • SomeNoneは、Optionから継承された2つcaseクラスである.
        //Some
        final case class Some[+A](@deprecatedName('x, "2.12.0") value: A) extends Option[A]
        
        //None
        case object None extends Option[Nothing]
    

    Optionの慣用としては集合とするかmonad、通過mapflatMapfilter・・とforeach:
        //   
        val name: Option[String] = request getParameter "name"
        val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
        println(upper getOrElse "")
        
        //         
        val upper = for {
          name 

    もう一つの習慣的な使い方は(あまりお勧めしません)パターンマッチングです.
        val nameMaybe = request getParameter "name"
        nameMaybe match {
          case Some(name) =>
            println(name.trim.toUppercase)
          case None =>
            println("No name value")
        }
    
  • Try
  • FailureSuccessは、Tryから継承された2つcaseクラスである.
        //Failure
        final case class Failure[+T](exception: Throwable) extends Try[T]
        
        //Success
        final case class Success[+T](value: T) extends Try[T]
    
    Try異常がある場合によく用いられ、Try発生する可能性のある異常を不確定に処理する.
        import scala.io.StdIn
        import scala.util.{Try, Success, Failure}
        def divide: Try[Int] = {
          val dividend = Try(StdIn.readLine("Enter an Int that you'd like to divide:
    ").toInt) val divisor = Try(StdIn.readLine("Enter an Int that you'd like to divide by:
    ").toInt) val problem = dividend.flatMap(x => divisor.map(y => x/y)) problem match { case Success(v) => println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v) Success(v) case Failure(e) => println("You must've divided by zero or entered something that's not an Int. Try again!") println("Info from the exception: " + e.getMessage) divide } }

    以上の例では、Tryの重要な特性の一つとして、Try配管機能を有すること、flatMapおよびmapそれらの成功した操作の結果をSuccessに包装し、それらの異常をFailureに包装し、recoverおよびrecoverWithに対しては、デフォルト対Failure結果がトリガーされることが分かる.