Create a gist now

Instantly share code, notes, and snippets.

@japgolly /eg.scala
Last active Aug 29, 2015

What would you like to do?
An Example of Functional Programming
// http://japgolly.blogspot.com.au/2014/09/an-example-of-functional-programming.html
sealed trait Validity
case object Valid extends Validity
case class Invalid(e1: String, en: List[String]) extends Validity
case class Validator[A](f: A => Validity) {
@inline final def apply(a: A) = f(a)
def +(v: Validator[A]) = Validator[A](a =>
(apply(a), v(a)) match {
case (Valid , Valid ) => Valid
case (Valid , e@ Invalid(_,_)) => e
case (e@ Invalid(_,_), Valid ) => e
case (Invalid(e1,en) , Invalid(e2,em) ) => Invalid(e1, en ::: e2 :: em)
})
def andIfSuccessful(v: Validator[A]) = Validator[A](a =>
apply(a) match {
case Valid => v(a)
case e@ Invalid(_,_) => e
})
}
object Validator {
def id[A] = Validator[A](_ => Valid)
def pred[A](f: A => Boolean, err: => String) =
Validator[A](a => if (f(a)) Valid else Invalid(err, Nil))
def regex(r: java.util.regex.Pattern, err: => String) =
pred[String](a => r.matcher(a).matches, err)
}
val nonEmpty = Validator.pred[String](_.nonEmpty, "must be empty")
val lowercase = Validator.regex("^[a-z]*$".r.pattern, "must be lowercase")
val containNumber = Validator.regex(".*[0-9].*".r.pattern, "must contain a number")
val usernameV = (nonEmpty andIfSuccessful containNumber) + lowercase
def buildErrorMessage(field: String, h: String, t: List[String]): String = t match {
case Nil => s"$field $h"
case _ => (h :: t).zipWithIndex.map{case (e,i) => s"${i+1}) $e"}.mkString(s"$field ", ", ", ".")
}
type Effect = () => Unit
def fakeSave: Effect = () => println("Fake save")
def example(u: String): Either[String, Effect] =
usernameV(u) match {
case Valid => Right(fakeSave)
case Invalid(h, t) => Left(buildErrorMessage("Username", h, t))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment