\/

LYAHFGG:

The Either e a type on the other hand, allows us to incorporate a context of possible failure to our values while also being able to attach values to the failure, so that they can describe what went wrong or provide some other useful info regarding the failure.

We know Either[A, B] from the standard library, but Scalaz 7 implements its own Either equivalent named \/:

sealed trait \/[+A, +B] {
...
/** Return `true` if this disjunction is left. */
def isLeft: Boolean =
this match {
case -\/(_) => true
case \/-(_) => false
}

/** Return `true` if this disjunction is right. */
def isRight: Boolean =
this match {
case -\/(_) => false
case \/-(_) => true
}
...
/** Flip the left/right values in this disjunction. Alias for `unary_~` */
def swap: (B \/ A) =
this match {
case -\/(a) => \/-(a)
case \/-(b) => -\/(b)
}
/** Flip the left/right values in this disjunction. Alias for `swap` */
def unary_~ : (B \/ A) = swap
...
/** Return the right value of this disjunction or the given default if left. Alias for `|` */
def getOrElse[BB >: B](x: => BB): BB =
toOption getOrElse x
/** Return the right value of this disjunction or the given default if left. Alias for `getOrElse` */
def |[BB >: B](x: => BB): BB = getOrElse(x)

/** Return this if it is a right, otherwise, return the given value. Alias for `|||` */
def orElse[AA >: A, BB >: B](x: => AA \/ BB): AA \/ BB =
this match {
case -\/(_) => x
case \/-(_) => this
}
/** Return this if it is a right, otherwise, return the given value. Alias for `orElse` */
def |||[AA >: A, BB >: B](x: => AA \/ BB): AA \/ BB = orElse(x)
...
}

private case class -\/[+A](a: A) extends (A \/ Nothing)
private case class \/-[+B](b: B) extends (Nothing \/ B)

These values are created using right and left method injected to all data types via IdOps:

scala> 1.right[String]
res12: scalaz.\/[String,Int] = \/-(1)

scala> "error".left[Int]
res13: scalaz.\/[String,Int] = -\/(error)

The Either type in Scala standard library is not a monad on its own, which means it does not implement flatMap method with or without Scalaz:

scala> Left[String, Int]("boom") flatMap { x => Right[String, Int](x + 1) }
<console>:8: error: value flatMap is not a member of scala.util.Left[String,Int]
Left[String, Int]("boom") flatMap { x => Right[String, Int](x + 1) }
^

You have to call right method to turn it into RightProjection:

scala> Left[String, Int]("boom").right flatMap { x => Right[String, Int](x + 1)}
res15: scala.util.Either[String,Int] = Left(boom)

This is silly since the point of having Either is to report an error on the left. Scalaz’s \/ assumes that you’d mostly want right projection:

scala> "boom".left[Int] >>= { x => (x + 1).right }
res18: scalaz.Unapply[scalaz.Bind,scalaz.\/[String,Int]]{type M[X] = scalaz.\/[String,X]; type A = Int}#M[Int] = -\/(boom)

This is nice. Let’s try using it in for syntax:

scala> for {
e1 <- "event 1 ok".right
e2 <- "event 2 failed!".left[String]
e3 <- "event 3 failed!".left[String]
} yield (e1 |+| e2 |+| e3)
res24: scalaz.\/[String,String] = -\/(event 2 failed!)

As you can see, the first failure rolls up as the final result. How do we get the value out of \/? First there’s isRight and isLeft method to check which side we are on:

scala> "event 1 ok".right.isRight
res25: Boolean = true

scala> "event 1 ok".right.isLeft
res26: Boolean = false

For right side, we can use getOrElse and its symbolic alias | as follows:

scala> "event 1 ok".right | "something bad"
res27: String = event 1 ok

For left value, we can call swap method or it’s symbolic alias unary_~:

scala> ~"event 2 failed!".left[String] | "something good"
res28: String = event 2 failed!

We can use map to modify the right side value:

scala> "event 1 ok".right map {_ + "!"}
res31: scalaz.\/[Nothing,String] = \/-(event 1 ok!)

To chain on the left side, there’s orElse, which accepts => AA \/ BB where [AA >: A, BB >: B]. The symbolic alias for orElse is |||:

scala> "event 1 failed!".left ||| "retry event 1 ok".right
res32: scalaz.\/[String,String] = \/-(retry event 1 ok)