### Ior データ型

Cats には `A``B` のペアを表すデータ型がもう1つあって、 Ior と呼ばれている。

``````/** Represents a right-biased disjunction that is either an `A`, or a `B`, or both an `A` and a `B`.
*
* An instance of `A [[Ior]] B` is one of:
*  - `[[Ior.Left Left]][A]`
*  - `[[Ior.Right Right]][B]`
*  - `[[Ior.Both Both]][A, B]`
*
* `A [[Ior]] B` is similar to `A [[Xor]] B`, except that it can represent the simultaneous presence of
* an `A` and a `B`. It is right-biased like [[Xor]], so methods such as `map` and `flatMap` operate on the
* `B` value. Some methods, like `flatMap`, handle the presence of two [[Ior.Both Both]] values using a
* `[[Semigroup]][A]`, while other methods, like [[toXor]], ignore the `A` value in a [[Ior.Both Both]].
*
* `A [[Ior]] B` is isomorphic to `(A [[Xor]] B) [[Xor]] (A, B)`, but provides methods biased toward `B`
* values, regardless of whether the `B` values appear in a [[Ior.Right Right]] or a [[Ior.Both Both]].
* The isomorphic [[Xor]] form can be accessed via the [[unwrap]] method.
*/
sealed abstract class Ior[+A, +B] extends Product with Serializable {

final def fold[C](fa: A => C, fb: B => C, fab: (A, B) => C): C = this match {
case Ior.Left(a) => fa(a)
case Ior.Right(b) => fb(b)
case Ior.Both(a, b) => fab(a, b)
}

final def isLeft: Boolean = fold(_ => true, _ => false, (_, _) => false)
final def isRight: Boolean = fold(_ => false, _ => true, (_, _) => false)
final def isBoth: Boolean = fold(_ => false, _ => false, (_, _) => true)

....
}

object Ior extends IorInstances with IorFunctions {
final case class Left[+A](a: A) extends (A Ior Nothing)
final case class Right[+B](b: B) extends (Nothing Ior B)
final case class Both[+A, +B](a: A, b: B) extends (A Ior B)
}
``````

これらの値は `Ior``left``right``both` メソッドを使って定義する:

``````import cats._, cats.data._, cats.syntax.all._

import cats.data.{ NonEmptyList => NEL }

Ior.right[NEL[String], Int](1)
// res0: Ior[NonEmptyList[String], Int] = Right(b = 1)

Ior.left[NEL[String], Int](NEL.of("error"))
// res1: Ior[NonEmptyList[String], Int] = Left(
//   a = NonEmptyList(head = "error", tail = List())
// )

Ior.both[NEL[String], Int](NEL.of("warning"), 1)
// res2: Ior[NonEmptyList[String], Int] = Both(
//   a = NonEmptyList(head = "warning", tail = List()),
//   b = 1
// )
``````

scaladoc コメントに書いてある通り、`Ior``flatMap``Ior.both(...)` 値をみると `Semigroup[A]` を用いて失敗値を累積 (accumulate) する。 そのため、これは `Xor``Validated` のハイブリッドのような感覚で使えるかもしれない。

`flatMap` の振る舞いを 9つ全ての組み合わせでみてみよう:

``````Ior.right[NEL[String], Int](1) >>=
{ x => Ior.right[NEL[String], Int](x + 1) }
// res3: Ior[NonEmptyList[String], Int] = Right(b = 2)

Ior.left[NEL[String], Int](NEL.of("error 1")) >>=
{ x => Ior.right[NEL[String], Int](x + 1) }
// res4: Ior[NonEmptyList[String], Int] = Left(
//   a = NonEmptyList(head = "error 1", tail = List())
// )

Ior.both[NEL[String], Int](NEL.of("warning 1"), 1) >>=
{ x => Ior.right[NEL[String], Int](x + 1) }
// res5: Ior[NonEmptyList[String], Int] = Both(
//   a = NonEmptyList(head = "warning 1", tail = List()),
//   b = 2
// )

Ior.right[NEL[String], Int](1) >>=
{ x => Ior.left[NEL[String], Int](NEL.of("error 2")) }
// res6: Ior[NonEmptyList[String], Int] = Left(
//   a = NonEmptyList(head = "error 2", tail = List())
// )

Ior.left[NEL[String], Int](NEL.of("error 1")) >>=
{ x => Ior.left[NEL[String], Int](NEL.of("error 2")) }
// res7: Ior[NonEmptyList[String], Int] = Left(
//   a = NonEmptyList(head = "error 1", tail = List())
// )

Ior.both[NEL[String], Int](NEL.of("warning 1"), 1) >>=
{ x => Ior.left[NEL[String], Int](NEL.of("error 2")) }
// res8: Ior[NonEmptyList[String], Int] = Left(
//   a = NonEmptyList(head = "warning 1", tail = List("error 2"))
// )

Ior.right[NEL[String], Int](1) >>=
{ x => Ior.both[NEL[String], Int](NEL.of("warning 2"), x + 1) }
// res9: Ior[NonEmptyList[String], Int] = Both(
//   a = NonEmptyList(head = "warning 2", tail = List()),
//   b = 2
// )

Ior.left[NEL[String], Int](NEL.of("error 1")) >>=
{ x => Ior.both[NEL[String], Int](NEL.of("warning 2"), x + 1) }
// res10: Ior[NonEmptyList[String], Int] = Left(
//   a = NonEmptyList(head = "error 1", tail = List())
// )

Ior.both[NEL[String], Int](NEL.of("warning 1"), 1) >>=
{ x => Ior.both[NEL[String], Int](NEL.of("warning 2"), x + 1) }
// res11: Ior[NonEmptyList[String], Int] = Both(
//   a = NonEmptyList(head = "warning 1", tail = List("warning 2")),
//   b = 2
// )
``````

`for` 内包表記からも使える:

``````for {
e1 <- Ior.right[NEL[String], Int](1)
e2 <- Ior.both[NEL[String], Int](NEL.of("event 2 warning"), e1 + 1)
e3 <- Ior.both[NEL[String], Int](NEL.of("event 3 warning"), e2 + 1)
} yield (e1 |+| e2 |+| e3)
// res12: Ior[NonEmptyList[String], Int] = Both(
//   a = NonEmptyList(head = "event 2 warning", tail = List("event 3 warning")),
//   b = 6
// )
``````

`Ior.left``Xor[A, B]``Either[A, B]` の失敗値のようにショート回路になるが、 `Ior.both``Validated[A, B]` のように失敗値を累積させる。