ソースレベルからEither、Option、Tryを解読

4866 ワード

scala
差異
  • 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結果がトリガーされることが分かる.