ScalaのEitherT[F[_], A, B]に入門する

仕事でScalaのコードを書いているのですが、EitherTをよく見かけるのでおさらいしておきたいと思います。

## EitherTとは
EitherTはEitherとF[Either]の軽量ラッパーで、EitherをOptionやFutureなどの型に内包する処理をする場合、EitherTを使うことで処理が簡便に書ける。
EitherTにはcatsやscalazといった関数型ライブラリに用意されていますが、ここではcatsのEitherTをメインに扱って行きたいと思います。

以下importします。

```scala
import cats.implicits._
import cats.data.Either
````

以下公式サイトの例です。
Future[Either[A, B]]を非同期で合成させています。

```scala
import scala.util.Try
import cats.implicits._

def parseDouble(s: String): Either[String, Double] =
  Try(s.toDouble).map(Right(_)).getOrElse(Left(s"$s is not a number"))

def divide(a: Double, b: Double): Either[String, Double] =
  Either.cond(b != 0, a / b, "Cannot divide by zero")

def parseDoubleAsync(s: String): Future[Either[String, Double]] =
  Future.successful(parseDouble(s))

def divideAsync(a: Double, b: Double): Future[Either[String, Double]] =
  Future.successful(divide(a, b))

// EitherTを使用しない場合
def divisionProgramAsync(inputA: String, inputB: String): Future[Either[String, Double]] =
  parseDoubleAsync(inputA) flatMap { eitherA =>
    parseDoubleAsync(inputB) flatMap { eitherB =>
      (eitherA, eitherB) match {
        case (Right(a), Right(b)) => divideAsync(a, b)
        case (Left(err), _) => Future.successful(Left(err))
        case (_, Left(err)) => Future.successful(Left(err))
      }
    }
  }

// EitherTを使用する場合
def divisionProgramAsync(inputA: String, inputB: String): EitherT[Future, String, Double] =
  for {
    a <- EitherT(parseDoubleAsync(inputA))
    b <- EitherT(parseDoubleAsync(inputB))
    result <- EitherT(divideAsync(a, b))
  } yield result

divisionProgramAsync("4", "2").value
// res2: Future[Either[String, Double]] = Future(Success(Right(2.0)))
divisionProgramAsync("a", "b").value

```
見てお分かりの通りEitherTを使用することでスッキリ書くことができていますし、EitherTを使用しない場合だとエラー処理とプログラムが一緒ごたになっています。
EitherT少し難しいと感じていましたがやはり必要なんだと感じました。

## AからEitherTへの変換

```scala
val num: Int = 5
val error: String = "Not a number"

numEitherT: EitherT[Option, String, Int] = EitherT.rightT(num) // EitherT(Some(Right(5)))
errorEitherT: EitherT[Option, String, Int] = EitherT.leftT(error)  // EitherT(Left("Not a number"))
```

## F[A] からEitherTへの変換

```scala
val numOpt = Option[Int] = Some(5)
val errOpt = Option[String] = Some("Not a number")

val numEitherT: EitherT[Option, String, Int] = EitherT.right(numOpt) // EitherT(Some(Right(5)))
val errEitherT: EitherT[Option, String, Int] = EitherT.left(error) // EitherT(Left("Not a number"))
```


## Either[A, B]またはF[Either[A, B]]からEitherTへの変換

```scala

val numE: Either[String, Int] = Right(100)
val errE: Either[String, Int] = Left("Not a number")
val numFE = List[Either[String, Int]]= List(Right(250)) 

val numET: EitherT[List String, Int] = EitherT.fromEither(numE) //EitherT(List(Right(100)))
val errEt: EitherT[List, String, Int] = EitherT.fromEither(errE) // EitherT(List(Left("Not a number")))
val numFET: EitherT[List, String, Int] = EitherT(numFE) // EitherT(List(Right(250)))
```

## EitherTからF[Either[A, B]]を抽出する

```scala
val errT: EitherT[Future, String, Int] = EitherT.left("hoge") // EitherT(Future(Success(Left("hoge"))))

val err: Future[Either[String, Int]] = errT.value // Future(Success(Left("hoge")))
```

cats公式リファレンスをもとにEitherTの理解を深めることができました。

少しむずかしい書き方をしていますが、EitherTを使用することで処理が簡便に書けることがわかりました。


[公式リファレンス](https://typelevel.org/cats/datatypes/eithert.html])