Last active
September 1, 2017 14:32
-
-
Save japgolly/fe822c84855da9b7f011 to your computer and use it in GitHub Desktop.
An Example of Functional Programming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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