Skip to content

Instantly share code, notes, and snippets.

@manjuraj
Last active November 12, 2018 16:15
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save manjuraj/c2d1a340d20678441652 to your computer and use it in GitHub Desktop.
scalaz disjunction
//
// Disjunction - aka Scalaz Either
// \/[A, B] is an alternative to Either[A, B]
// -\/ is Left (usually represents failure by convention)
// \/- is Right (usually represents success by convention)
// Left or Right - which side of the Disjunction does the "-" appear?
//
// Prefer infix notation to express Disjunction Type v: String \/ Double
//
// References
// - http://eed3si9n.com/learning-scalaz/Either.html
//
// A common use of a disjunction is to explicitly represent the
// possibility of failure in a result as opposed to throwing an
// exception. By convention, the Left -\/ is used for errors and
// the right \/- is reserved for successes. For example, a function
// that attempts to parse an integer from a string may have a
// return type of \/[NumberFormatException, Int]. However, since
// there is no need to actually throw an exception, the type A chosen
// for the "left" could be any type representing an error and has
// no need to actually extend Exception
//
// \/[A, B] is isomorphic to scala.Either[A, B], but \/ is
// is right-biased, so methods such as `map` and `flatMap` apply
// only in the context of the "right" case. This right bias makes
// \/ more convenient to use than scala.Either in a monadic
// context. Methods such as `swap`, `swapped`, and `leftMap` provide
// functionality that `scala.Either` exposes through left projections
//
// \/[A, B\ is also isomorphic to Validation[A, B]. The subtle but
// important difference is that Applicative instances for Validation
// accumulates errors ("lefts") while Applicative instances for \/
// "fail fast" on the first "left" they evaluate. This fail-fast
// behavior allows \/ to have lawful Monad instances that are consistent
// with their Applicative instances, while Validation cannot.
//
//
// Builders:
// - using the disjunction singleton instances \/- and -\/
// - left method
// - right method
// - fromEither method
//
import scalaz.{ \/, -\/, \/- }
scala> \/-("right")
res4: scalaz.\/-[String] = \/-(right)
scala> -\/("left")
res5: scalaz.-\/[String] = -\/(left)
scala> \/.right("right")
res8: scalaz.\/[Nothing,String] = \/-(right)
scala> \/.left("left")
res9: scalaz.\/[String,Nothing] = -\/(left
//
// Isomorphism: Either[A, B] <> \/[A, B]
//
scala> val e = \/.fromEither(Right("right"))
res0: scalaz.\/[Nothing,String] = \/-(right)
scala> e.toEither
res1: Either[Nothing,String] = Right(right)
scala> val e = \/.fromEither(Left("left"))
res2: scalaz.\/[String,Nothing] = -\/(left)
scala> e.toEither
res3: Either[String,Nothing] = Left(left)
import scalaz.syntax.std.either._
scala> Left("left").disjunction
res1: scalaz.\/[String,B] = -\/(left)
scala> Right("right").disjunction
res2: scalaz.\/[A,String] = \/-(right)
//
// Try[A] => \/[Throwable, A]
//
scala> \/.fromTryCatchNonFatal[Int](throw new RuntimeException("runtime error"))
res4: scalaz.\/[Throwable,Int] = -\/(java.lang.RuntimeException: runtime error)
scala> \/.fromTryCatchNonFatal[Int](1 / 0)
res6: scalaz.\/[Throwable,Int] = -\/(java.lang.ArithmeticException: / by zero)
import scalaz._
import scalaz.syntax.either._
scala> "right".right
res2: scalaz.\/[Nothing,String] = \/-(right)
scala> "left".left
res3: scalaz.\/[String,Nothing] = -\/(left)
scala> "right".right[Int]
res8: scalaz.\/[Int,String] = \/-(right)
scala> "left".left[Double]
res10: scalaz.\/[String,Double] = -\/(left)
scala> for {
| a <- "a".right[String]
| b <- "b".right[String]
| } yield (a, b)
res5: scalaz.\/[String,(String, String)] = \/-((a,b))
scala> for {
| a <- "a".right[String]
| e <- "e".left[Int]
| } yield (a, e)
res7: scalaz.\/[String,(String, Int)] = -\/(e)
scala> for {
| a <- "a".right[String]
| e <- "e".left[Int]
| f <- "f".left[Int]
| } yield (a, e, f)
res11: scalaz.\/[String,(String, Int, Int)] = -\/(e)
import scalaz._
import scalaz.std.either._
//
// Option[A] => \/[E, A]
// \/>[E]
// toRightDisjunction[E](e: => E): E \/ A = o.toRight(self)(e)
//
// toLeftDisjunction[A]
// <\/[A]
import scalaz._
import syntax.std.option._
scala> Some(10) \/> "message"
res0: scalaz.\/[String,Int] = \/-(10)
scala> Some(10).toRightDisjunction("message")
res1: scalaz.\/[String,Int] = \/-(10)
scala> None \/> "message"
res2: scalaz.\/[String,A] = -\/(message)
scala> None.toRightDisjunction("message")
res3: scalaz.\/[String,A] = -\/(message)
scala> Some(10) <\/ "message"
res4: scalaz.\/[Int,String] = -\/(10)
scala> Some(10).toLeftDisjunction("message")
res5: scalaz.\/[Int,String] = -\/(10)
scala> None <\/ "message"
res6: scalaz.\/[A,String] = \/-(message)
scala> None.toLeftDisjunction("message")
res7: scalaz.\/[A,String] = \/-(message)
import scalaz._
import scalaz.std.list._
import scalaz.syntax.traverse._
import scalaz.syntax.either._
scala> def f(x: Int): \/[String, Int] = if (x > 2) x.right[String] else "failure".left[Int]
f: (x: Int)scalaz.\/[String,Int]
scala> List(1, 2, 3).traverseU(f)
res1: scalaz.\/[String,List[Int]] = -\/(failure)
scala> List(3, 4, 5).traverseU(f)
res2: scalaz.\/[String,List[Int]] = \/-(List(3, 4, 5))
scala> List(3, 4, 5).map(f).sequenceU
res9: scalaz.\/[String,List[Int]] = \/-(List(3, 4, 5))
scala> List(1, 2, 3).map(f).sequenceU
res3: scalaz.\/[String,List[Int]] = -\/(failure)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment