136 lines
3.7 KiB
Scala
136 lines
3.7 KiB
Scala
package green.thisfieldwas.embracingnondeterminism.data




/** The `Either` class encodes a dimension contextualized by "Either" the


* "Right" term that you want, or you're "Left" with the term you don't want.


* This context is frequently used to model success or failure, the specific


* instance of which is unknown at runtime without specific knowledge of the


* inputs to the operation producing the context.


*


* The presence of the "Right" term `A` is the desired case of this context. If


* the "Left" term `X` is present, then the context is in the undesired case.


*


* @tparam X


* The "Left" term, or undesired case.


* @tparam A


* The "Right" term, or desired case.


*/


sealed trait Either[+X, +A] {




/** Gets the instance of the "Left" term if it exists.


*


* @return


* The "Left" instance.


* @throws NoSuchElementException


* if the context is a "Right".


*/


def left: X




/** Gets the instance of the "Right" term if it exists.


*


* @return


* The "Right" instance.


* @throws NoSuchElementException


* if the context is a "Left".


*/


def right: A




/** Gets whether this context is a "Left".


*


* @return


* True if left.


*/


def isLeft: Boolean = false




/** Gets whether this context is a "Right".


*


* @return


* True if right.


*/


def isRight: Boolean = false


}




/** Encodes the "Left" or undesired case of the context.


*


* @param left


* The instance of the "Left" term.


* @tparam X


* The "Left" term, or undesired case.


* @tparam A


* The "Right" term, or desired case.


*/


case class Left[+X, +A](left: X) extends Either[X, A] {




def right: A = throw new NoSuchElementException("Left.right")




override def isLeft: Boolean = true


}




/** Encodes the "Right" or desired case of the context.


*


* @param right


* The instance of the "Right" case.


* @tparam X


* The "Left" term, or undesired case.


* @tparam A


* The "Right" term, or desired case.


*/


case class Right[+X, +A](right: A) extends Either[X, A] {




def left: X = throw new NoSuchElementException("Right.left")




override def isRight: Boolean = true


}




object Either {




/** A smart constructor to create a "Left" typed specifically as an "Either".


*


* @param x


* The "Left" instance.


* @tparam X


* The "Left" term, or undesired case.


* @tparam A


* The "Right" term, or desired case.


* @return


* An "Either" in the undesired case.


*/


def left[X, A](x: X): Either[X, A] = Left(x)




/** A smart constructor to create a "Right" typed specifically as an "Either".


*


* @param a


* The "Right" instance.


* @tparam X


* The "Left" term, or undesired case.


* @tparam A


* The "Right" term, or desired case.


* @return


* An "Either" in the desired case.


*/


def right[X, A](a: A): Either[X, A] = Right(a)




implicit def eitherFunctor[X]: Functor[Either[X, *]] = new Functor[Either[X, *]] {




/** Given a structure `F[A]` and function `f: A => B`, if the structure is


* in the desired case, then apply the function such that `F[A] => F[B]`.


* If the structure `F[A]` is in the undesired case, then propagate the


* undesired case as `F[B]`.


*


* @param fa


* The instance of the structure to apply the function to.


* @param f


* The function to apply, if the structure is in the desired case.


* @tparam A


* The type of instances contained within the structure.


* @tparam B


* The type to map the instances into.


* @return


* The resulting structure.


*/


override def map[A, B](fa: Either[X, A])(f: A => B): Either[X, B] = fa match {


case Left(x) => Left(x)


case Right(a) => Right(f(a))


}


}


}
