FP in scala 勉強メモ [PartⅠ 第4章 例外を使わないエラー処理]


# 第4章 例外を使わないエラー処理

全体的な考えた方としては、失敗や例外を通常の値で表し、エラー処理とリカバリに共通するパターンを高階関数として抽出する

評価されない式をサンク(thunk)と呼ぶ。

i: => Int構文で渡す高階関数はデフォルトキャッシュされない。つまり参照されるたびに実行される。
キャッシュしたい場合はlazy valで参照することにより、明示的にキャッシュする。

Scalaの非正格関数は引数を値渡しではなく名前渡しで受け取る。


## EXERCISE 4.1

```
// Q
リスト4ｰ4のすべての関数をOptionで実装せよ。
各関数を実装するときに、その関数の意味と、それを使用するであろう状況について考えること。
どの関数をいつ使用するかについては、後ほど考察する。
ヒントは以下のとおり。
 - パターンマッチングを使用しても構わないが、
　  mapとgetOrElseを除く関数はすべてパターンマッチングに頼らなくても実装できるはずである。
 -  map,flatMapの実装は、過多シグネチャの情報に基づいて充分に決定できるはずである。
 -  getOrElseはOptionがNoneのバアアイは、指定されたデフォルト値を返す。
 -  orElseは、1つ目のOptionが定義されている場合はそれを返し、そうでない場合は2つ目のOptionを返す。

// answer
  def map[B](f: A => B): Option[B] = this match {
    case None => None
    case Some(x) => Some(f(x))
  }

 def getOrElse[B>:A](default: => B): B = this match {
    case None => default
    case Some(x) => x  
  }

 def flatMap[B](f: A => Option[B]): Option[B] = map(f).getOrElse(None)

 def orElse[B>:A](ob: => Option[B]): Option[B] = this match {
    case None => ob
    case Some(x) => Some(x)  
  }

  def filter(f: A => Boolean): Option[A] = this match {
    case Some(x) if f(x) => Some(x)
    case None => None
  }

// 公式のanswer
 def map[B](f: A => B): Option[B] = this match {
    case None => None
    case Some(a) => Some(f(a))
  }

  def getOrElse[B>:A](default: => B): B = this match {
    case None => default
    case Some(a) => a
  }

  def flatMap[B](f: A => Option[B]): Option[B] =
    map(f) getOrElse None

  /*
  Of course, we can also implement `flatMap` with explicit pattern matching.
  */
  def flatMap_1[B](f: A => Option[B]): Option[B] = this match {
    case None => None
    case Some(a) => f(a)
  }

  def orElse[B>:A](ob: => Option[B]) =
    this map (Some(_)) getOrElse ob

  /*
  Again, we can implement this with explicit pattern matching.
  */
  def orElse_1[B>:A](ob: => Option[B]): Option[B] = this match {
    case None => ob
    case _ => this
  }

  def filter(f: A => Boolean): Option[A] = this match {
    case Some(a) if f(a) => this
    case _ => None
  }
  /*
  This can also be defined in terms of `flatMap`.
  */
  def filter_1(f: A => Boolean): Option[A] =
    flatMap(a => if (f(a)) Some(a) else None)
```

orElse,filterは諦めてmatch caseで実装


## EXERCISE 4.2

```
// Q
flatMapをベースとしてvariance関数を実装せよ。
シーケンスの平均をm、シーケンスの各要素をxとすれば、分散はmath.pow(x - m, 2)の平均となる。
def variance(xs: Seq[Double]): Option[Double]

// answer


// 公式のanswer
  def mean(xs: Seq[Double]): Option[Double] =
    if (xs.isEmpty) None
    else Some(xs.sum / xs.length)

  def variance(xs: Seq[Double]): Option[Double] =
    mean(xs) flatMap (m => mean(xs.map(x => math.pow(x - m, 2))))

```

ぎぶ


## EXERCISE 4.3

```
// Q
2項関数を使って、Option型の2つの値を結合する総称関数map2を記述せよ。
どちらかのOption値がNoneの場合は、戻り値もNoneになる。
シグネチャは以下のとおり。
def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C]

// answer
  def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = (a,b) match {
    case (Some(x), Some(y)) => Some(f(x,y))
    case _ => None
  }

// 公式のanswer
def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] =
    a flatMap (aa => b map (bb => f(aa, bb)))
```

公式のflatMap & mapのほうがかっこいい


## EXERCISE 4.4

```
// Q
Optionのリストを1つのOptionにまとめるsequence関数を記述せよ。
新しいOptionには、元のリストに含まれているすべてのSome値のリストが含まれる。
元のリストにNoneが1つでも含まれていた場合、この関数の結果はNoneになる。
それ以外の場合は、すべての値のリストを含んだSomeになる。シグネチャは以下のとおり。
def sequence[A](a: List[Option[A]]): Option[List[A]]

// answer
 def sequence[A](a: List[Option[A]]): Option[List[A]] = {
    @annotation.tailrec
    def loop(l: List[Option[A]], x: Option[List[A]]): Option[List[A]] = {
      l match {
        case Some(h) :: List() => x.map(h :: _)
        case Some(h) :: t => loop(t, x.map(h :: _))
        case None :: _ => None
        case _ => println("_"); None
      }
    }
    loop(a, Some(List())).map(_.reverse)
  }

// 公式のanswer
  def sequence[A](a: List[Option[A]]): Option[List[A]] =
    a match {
      case Nil => Some(Nil)
      case h :: t => h flatMap (hh => sequence(t) map (hh :: _))
    }
  /*
  It can also be implemented using `foldRight` and `map2`. The type annotation on `foldRight` is needed here; otherwise
  Scala wrongly infers the result type of the fold as `Some[Nil.type]` and reports a type error (try it!). This is an
  unfortunate consequence of Scala using subtyping to encode algebraic data types.
  */
  def sequence_1[A](a: List[Option[A]]): Option[List[A]] =
    a.foldRight[Option[List[A]]](Some(Nil))((x,y) => map2(x,y)(_ :: _))

```

2回走査していてイケてない。。


## EXERCISE 4.5

```
// Q
traverse関数を実装せよ。mapとsequenceを利用すれば簡単だが、リストを1回だけ調べる、より効率のよい実装にすること。
要するに、traverseの観点からsequenceを実装すれば良い。

// answer
  def traverse[E,B,C](a: List[A])(f: A => Either[E,B]): Either[E,List[B]] =
    a match {
      case Nil => Right(Nil)
      case h :: t => (f(h) map2 traverse(t)(f))(_ :: _) 
  }

// 公式のanswer
  def traverse[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = 
    es match {
      case Nil => Right(Nil)
      case h::t => (f(h) map2 traverse(t)(f))(_ :: _)
    }
  
  def traverse_1[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = 
    es.foldRight[Either[E,List[B]]](Right(Nil))((a, b) => f(a).map2(b)(_ :: _))
```

4.4を参考にした。

## EXERCISE 4.6

```
// Q
Right値を操作するmap,flatMap,orElse,map2をEitherに追加せよ。
trait Either[+E,+A] {
 def map[B](f: A => B): Either[E, B] = sys.error("todo")

 def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = sys.error("todo")

 def orElse[EE >: E, B >: A](b: => Either[EE, B]): Either[EE, B] = sys.error("todo")

 def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] = sys.error("todo")
}
// answer
 trait Either[+E, +A] {
  def map[B](f: A => B): Either[E, B] = this match {
    case Right(x) => Right(f(x))
    case Left(e) => Left(e)
  }

  def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = this match {
    case Right(x) => f(x)
    case Left(e) => Left(e)
  }


  def orElse[EE >: E, B >: A](b: => Either[EE, B]): Either[EE, B] = this match {
    case Right(x) => this
    case Left(e) => b
  }

  def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] = for { x <- this; y <- b } yield f(x,y)

}
// 公式のanswer
sealed trait Either[+E,+A] {
 def map[B](f: A => B): Either[E, B] = 
   this match {
     case Right(a) => Right(f(a))
     case Left(e) => Left(e)
   }
   
 def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] =
   this match {
     case Left(e) => Left(e)
     case Right(a) => f(a)
   }
 def orElse[EE >: E, AA >: A](b: => Either[EE, AA]): Either[EE, AA] =
   this match {
     case Left(_) => b
     case Right(a) => Right(a)
   }
 def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): 
   Either[EE, C] = for { a <- this; b1 <- b } yield f(a,b1)
}
```

map2が合ってたの気持ち良い^q^


## EXERCISE 4.7

```
// Q
Eitherでsequenceとtraverseを実装せよ。
これらは、エラーが発生した場合に、最初に検出されたエラーを返すものとする。

def sequence[E,A](es: List[Either[E,A]]): Either[E,List[A]]
def traverse[E,A,B](as: List[A])(f: A => Either[E,B]): Either[E,List[B]]

// answer
  def traverse[E, A, B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] =
    es match {
      case Nil => Right(Nil)
      case h :: t => (f(h) map2 traverse(t)(f))(_ :: _)
    }

  def sequence[E, A](es: List[Either[E, A]]): Either[E, List[A]] =
    es match {
      case Nil => Right(Nil)
      case Left(h) :: _  => Left(h)
      case h :: t => h flatMap (hh => sequence(t) map (hh :: _))
    }

// 公式のanswer
  def traverse[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = 
    es match {
      case Nil => Right(Nil)
      case h::t => (f(h) map2 traverse(t)(f))(_ :: _)
    }
  
  def traverse_1[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = 
    es.foldRight[Either[E,List[B]]](Right(Nil))((a, b) => f(a).map2(b)(_ :: _))
  
  def sequence[E,A](es: List[Either[E,A]]): Either[E,List[A]] = 
    traverse(es)(x => x)

```

sequenceかっこよい・・。

## EXERCISE 4.8

```
// Q
リスト4-10の実装では、名前と年齢が両方とも無効であったとしても、map2はエラーを1つしか報告できない。
両方のエラーを報告するには、何を変更する必要があるか。
map2を変更するのか、それともmkPersonのシグネチャを変更するのか。
あるいは、新しい構造を追加して、この要件をEitherよりも効果的に満たす新しいデータ型を作成することは可能か。
そのデータ型では、orElse、travase、sequenceの振る舞いはどのように変化するか。

// 公式のanswer
There are a number of variations on Option and Either. If we want to accumulate multiple errors, a simple
approach is a new data type that lets us keep a list of errors in the data constructor that represents failures:
trait Partial[+A,+B]
case class Errors+A extends Partial[A,Nothing]
case class Success+B extends Partial[Nothing,B]
There is a type very similar to this called Validation in the Scalaz library. You can implement map, map2,
sequence, and so on for this type in such a way that errors are accumulated when possible (flatMap is unable to
accumulate errors--can you see why?). This idea can even be generalized further--we don't need to accumulate failing
values into a list; we can accumulate values using any user-supplied binary function.
It's also possible to use Either[List[E],_] directly to accumulate errors, using different implementations of
helper functions like map2 and sequence.


```