EitherT[Scala with Cats]の説明

#初めに
<b>`EitherT`の簡単な説明…`EitherT[F[_], A, B]`は、`F[Either[A, B]]`の軽量ラッパーです。`Either`を`Option`や`Future`などの型内に配置するような処理をする際、`EitherT`を使うことによりコードをスッキリまとめやすくなります。</b>

Scala with Catsの`EitherT`について[公式ドキュメント](https://typelevel.org/cats/datatypes/eithert.html)を自分なりに翻訳して説明していきます！！
※ところどころ、分かりやすいように文を付け加えてます！

#まえがき
`Either`は、ほとんどの状況のエラー処理に使用できます。ただし、 `Either`を`Option`や`Future`などの効果のある型内に配置すると、エラーを処理するために大量の<b>ボイラープレート</b>(仕様上省略不能で、かつほとんど変更を加えることなく多くの場所に組み込む必要があるソースコードのこと。[[Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9C%E3%82%A4%E3%83%A9%E3%83%BC%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88)より])が必要になります。 たとえば、次のプログラムについて考えてみます。

```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 divisionProgram(inputA: String, inputB: String): Either[String, Double] =
  for {
    a <- parseDouble(inputA)
    b <- parseDouble(inputB)
    result <- divide(a, b)
  } yield result

divisionProgram("4", "2") // Right(2.0)
// res0: Either[String, Double] = Right(2.0) // Right(2.0)
divisionProgram("a", "b") // Left("a is not a number")
// res1: Either[String, Double] = Left("a is not a number")
```

`parseDouble`と`divide`が非同期になるように書き直され、代わりに`Future[Either[String, Double]]`を返すとします。`DivisionProgram`は`Future`と`Either`を一緒に構成する必要があるため、for-comprehensionは使用できなくなりました。つまり、適切な型が返されるように、エラー処理を明示的に実行する必要があります。

```Scala
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

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))

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))
      }
    }
  }
```

更新されたコードは明らかにより読みにくく冗長になっています。プログラムの詳細がエラー処理と混ざり合っています。そして、`Either`と`Future`がさらに増えると、エラーを適切に処理するために必要なボイラープレートの量が劇的に増加します。

#EitherT
`EitherT[F[_], A, B]`は、`F[Either[A, B]]`の軽量ラッパーであり、`Either`と`F`を簡単に作成できます。`EitherT`を使用するには、`Either`、`F`、`A`、および`B`の値が最初に`EitherT`に変換され、結果の`EitherT`値がコンビネータを使用して合成されます。例えば、非同期除算プログラムは次のように書き直すことができます。

```Scala
import cats.data.EitherT
import cats.implicits._

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
// res3: Future[Either[String, Double]] = Future(Success(Left(a is not a number)))
```

`F`がモナドの場合、`EitherT`もモナドを形成し、`flatMap`などのモナドコンビネータを使用して`EitherT`値を作成できることに注意してください。

#`A`または`B`から`EitherT[F, A, B]`へ
`A`または`B`が指定されたときに`EitherT`のleftバージョンまたはrightバージョンを取得するには、`EitherT.leftT`および`EitherT.rightT`（`EitherT.pure`と同意）をそれぞれ使用します。

```Scala
val number: EitherT[Option, String, Int] = EitherT.rightT(5)
val error: EitherT[Option, String, Int] = EitherT.leftT("Not a number")
```

#`F[A]`または`F[B]`から`EitherT[F, A, B]`へ
同様に、`EitherT.left`と`EitherT.right`を使用して、`F[A]`または`F[B]`を`EitherT`に変換します。また、`EitherT.right`と同意なメソッドとして`EitherT.liftF`を使用することもできます。

```Scala
val numberO: Option[Int] = Some(5)
val errorO: Option[String] = Some("Not a number")

val number: EitherT[Option, String, Int] = EitherT.right(numberO)
val error: EitherT[Option, String, Int] = EitherT.left(errorO)
```

#`Either[A, B]`または`F[Either[A, B]]`から`EitherT[F, A, B]`へ
`EitherT.fromEither`を使用して、`Either[A, B]`の値を`EitherT[F, A, B]`にリフトします。`F[Either[A, B]]`は、`EitherT`コンストラクタを使用して`EitherT`に変換できます。

```Scala
val numberE: Either[String, Int] = Right(100)
val errorE: Either[String, Int] = Left("Not a number")
val numberFE: List[Either[String, Int]] = List(Right(250))

val numberET: EitherT[List, String, Int] = EitherT.fromEither(numberE)
val errorET: EitherT[List, String, Int] = EitherT.fromEither(errorE)
val numberFET: EitherT[List, String, Int] = EitherT(numberFE)
```

#`Option[B]`または`F[Option[B]]`から`EitherT[F, A, B]`へ
`Option[B]`または`F[Option[B]]`は、デフォルト値とともに、`EitherT.fromOption`および`EitherT.fromOptionF`にそれぞれ渡して、`EitherT`を生成できます。`F[Option[B]]`およびデフォルトの`F[A]`には、`EitherT.fromOptionM`があります。

```Scala
val myOption: Option[Int] = None
// myOption: Option[Int] = None
val myOptionList: List[Option[Int]] = List(None, Some(2), Some(3), None, Some(5))
// myOptionList: List[Option[Int]] = List(
//   None,
//   Some(2),
//   Some(3),
//   None,
//   Some(5)
// )

val myOptionET = EitherT.fromOption[Future](myOption, "option not defined")
// myOptionET: EitherT[Future, String, Int] = EitherT(
//   Future(Success(Left(option not defined)))
// )
val myOptionListET = EitherT.fromOptionF(myOptionList, "option not defined")
// myOptionListET: EitherT[List, String, Int] = EitherT(
//   List(
//     Left("option not defined"),
//     Right(2),
//     Right(3),
//     Left("option not defined"),
//     Right(5)
//   )
// )
val myOptionListETM = EitherT.fromOptionM(myOptionList, List("option not defined"))
// myOptionListETM: EitherT[List, String, Int] = EitherT(
//   List(
//     Left("option not defined"),
//     Right(2),
//     Right(3),
//     Left("option not defined"),
//     Right(5)
//   )
// )
```

#`ApplicativeError[F, E]`から`EitherT[F, E, A]`へ
`ApplicativeError[F, E]`または`MonadError[F, E]`は、`attemptT`メソッドを使用して`EitherT[F, E, A]`に変換できます。

```Scala
val myTry: Try[Int] = Try(2)
val myFuture: Future[String] = Future.failed(new Exception())

val myTryET: EitherT[Try, Throwable, Int] = myTry.attemptT
val myFutureET: EitherT[Future, Throwable, String] = myFuture.attemptT
```

#`EitherT[F, A, B]`から`F[Either[A, B]]`を抽出する
基になる`F[Either[A, B]]`を取得するには、`EitherT`で定義された`value`メソッドを使用します。

```Scala
val errorT: EitherT[Future, String, Int] = EitherT.leftT("foo")
// errorT: EitherT[Future, String, Int] = EitherT(Future(Success(Left(foo))))

val error: Future[Either[String, Int]] = errorT.value
// error: Future[Either[String, Int]] = Future(Success(Left(foo)))
```

以上で[公式ドキュメント](https://typelevel.org/cats/datatypes/eithert.html)よりScala with CatsのEitherTについて説明終わりです！

ありがとうございました！！🙇‍♂️