Skip to content

Instantly share code, notes, and snippets.

@Chandler
Last active August 29, 2015 13:56
Show Gist options
  • Save Chandler/8930439 to your computer and use it in GitHub Desktop.
Save Chandler/8930439 to your computer and use it in GitHub Desktop.
Either Builder
sealed trait NotSatisfied { def reason: String }
case class MissingOption(reason: String) extends NotSatisfied
case class IncorrectBoolean(reason: String) extends NotSatisfied
/**
* Convert Option[T] to Either.RightProjection[MissingOption, T]
* for use in a for comprehension
*
* @param opt the option to consider
* @param e the error message string
* @return Either.RightProjection[MissingOption, T]
*/
class OptionToEither[T](opt: Option[T]) {
def orError(e: String): Either.RightProjection[MissingOption, T] = {
opt match {
case Some(t) => Right(t).right
case None => Left(MissingOption(e)).right
}
}
}
/**
* Convert Boolean to Either.RightProjection[IncorrectBoolean, T]
* for use in a for comprehension
*
* @param found the boolean to consider
* @param required the required boolean
* @param e the error message string
* @return Either.RightProjection[IncorrectBoolean, Boolean]
*/
class BooleanToEither(found: Boolean) {
def mustBe(required: Boolean, e: String): Either.RightProjection[IncorrectBoolean, Boolean] = {
if(found == required)
Right(found).right
else
Left(IncorrectBoolean(e)).right
}
}
/**
* Implicit method for converting Option[T] to OptionToEither
*
* Allows you to call myOption.onError("a error message")
* in a for comprehension and get back Either.RightProjection[MissingOption, T]
*
* @param opt the option to consider
* @return OptionToEither
*/
implicit def BuildOptionToEither[T](opt: Option[T]) = new OptionToEither(opt)
/**
* Implicit method for converting Boolean to OptionToBoolean
* Allows you to call myBool.mustBe(true, "the value wasn't true")
* in a for comprehension and get back Either.RightProjection[IncorrectBoolean, Boolean]
*
* @param bool the boolean to consider
* @return BooleanToEither
*/
implicit def BuildBooleanToEither(bool: Boolean) = new BooleanToEither(bool)
//Example of how to use Eithers to get error messages out of a for comprehension
case class Person(
name: Option[String],
age: Option[Int]) {
}
def isTeenager(age: Int): Boolean = age > 12 && age < 20
val people = List(
new Person(name = Some("alex"), age = Some(10)),
new Person(name = Some("sasha"), age = Some(16)),
new Person(name = None, age = Some(18)),
new Person(name = Some("chrissy"), age = None),
new Person(name = Some("mike"), age = Some(15)),
new Person(name = None, age = Some(100))
)
/**
* Return the name of a person if they're a teenager,
* otherwise return an error message explaining why
* they aren't a teenager.
*
* @param person a Person to check for being teenage
* @return an either containing the name or the error message
*/
def nameOfTeenager(person: Person): Either[NotSatisfied,String] =
for {
name <- person.name.orError("missing name") //if person.name is a none, return this error message
age <- person.age.orError("missing age") //same for person.age
_ <- isTeenager(age).mustBe(true, "is not a teenager") //isTeeanger must be true, otherwise return an error message
} yield {
name
}
val (names, errors) = people
.map { nameOfTeenager(_) }
.partition{ _.isRight }
//Teenagers: List(mike, sasha)
names.collect { case Right(t) => t }
//Errors: List(is not a teenager, missing name, missing age, missing name)
errors.collect { case Left(t) => t }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment