Skip to content

Instantly share code, notes, and snippets.

@dbuschman7
Created April 4, 2016 20:13
Show Gist options
  • Save dbuschman7/1cb98219c13b2381116a640d3ff95922 to your computer and use it in GitHub Desktop.
Save dbuschman7/1cb98219c13b2381116a640d3ff95922 to your computer and use it in GitHub Desktop.
package io.timeli.test
import scala.util.matching.Regex
trait Semigroup[A] {
def append(x: A)(y: A): A
}
object Semigroup {
implicit def listSemigroup[A]: Semigroup[List[A]] =
new Semigroup[List[A]] {
def append(x: List[A])(y: List[A]): List[A] = x ++ y
}
}
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
object Functor {
implicit class FunctorOps[A, F[_]: Functor](fa: F[A]) {
def map[B](f: A => B): F[B] = implicitly[Functor[F]].map(fa)(f)
}
}
object FunctorExtra {
implicit class FlippedFunctorOps[A, B](f: A => B) {
def <%>[F[_]: Functor](fa: F[A]): F[B] =
implicitly[Functor[F]].map(fa)(f)
}
}
trait Applicative[F[_]] extends Functor[F] {
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
}
object Applicative {
implicit class ApplicativeOps[A, F[_]: Applicative](fa: F[A]) {
def ap[B](ff: F[A => B]): F[B] =
implicitly[Applicative[F]].ap(ff)(fa)
}
}
object ApplicativeExtra {
implicit class FlippedApplicativeOps[A, B, F[_]: Applicative](ff: F[A => B]) {
def <*>(fa: F[A]): F[B] = implicitly[Applicative[F]].ap(ff)(fa)
}
}
sealed trait Validation[E, A]
case class Success[E, A](value: A) extends Validation[E, A]
case class Failure[E, A](value: E) extends Validation[E, A]
object Validation {
implicit def applicative[E: Semigroup]: Applicative[({ type λ[α] = Validation[E, α] })#λ] =
new Applicative[({ type λ[α] = Validation[E, α] })#λ] {
def map[A, B](fa: Validation[E, A])(f: A => B): Validation[E, B] =
fa match {
case Success(a) => Success(f(a))
case Failure(es) => Failure(es)
}
def ap[A, B](ff: Validation[E, A => B])(fa: Validation[E, A]): Validation[E, B] = {
(fa, ff) match {
case (Success(a), Success(f)) => Success(f(a))
case (Failure(es), Success(f)) => Failure(es)
case (Failure(es), Failure(e2)) => Failure(implicitly[Semigroup[E]].append(e2)(es))
case (Success(a), Failure(es)) => Failure(es)
}
}
}
}
case class User private (name: String, email: String, phone: String)
object User {
type Parsed[A] = Validation[List[String], A]
private def parseR(n: String, x: String, r: Regex): Parsed[String] =
r.findFirstIn(x) match {
case Some(_) => Success(x)
case _ => Failure(List(s"""invalid ${n}: "${x}""""))
}
import Functor.FunctorOps
import Applicative.ApplicativeOps
def parse(name: String, email: String, phone: String): Parsed[User] =
parseR("name", name, """\w+(\s\w+)*""".r)
.ap(parseR("email", email, """[^@]+@[^@]+""".r)
.ap(parseR("phone", phone, """\d{3}-\d{3}-\d{4}""".r)
.map((User.apply _).curried)))
import FunctorExtra.FlippedFunctorOps
import ApplicativeExtra.FlippedApplicativeOps
def parse2(name: String, email: String, phone: String): Parsed[User] =
(User.apply _).curried <%>
parseR("name", name, """\w+(\s\w+)*""".r) <*>
parseR("email", email, """[^@]+@[^@]+""".r) <*>
parseR("phone", phone, """\d{3}-\d{3}-\d{4}""".r)
}
object Main extends App {
def demo(parse: (String, String, String) => User.Parsed[User], name: String, email: String, phone: String): Unit = {
println(s"""parse("${name}", "${email}", "${phone}"):""")
parse(name, email, phone) match {
case Success(x) => println(s" ${x}\n")
case Failure(es) => println(es.mkString(" ", "\n ", "\n"))
}
}
println()
demo(User.parse _, "", "", "")
demo(User.parse _, "James", "james", "555-JAMES-42")
demo(User.parse _, "James", "[email protected]", "555-JAMES-42")
demo(User.parse _, "James", "[email protected]", "555-123-4567")
demo(User.parse2 _, "", "", "")
demo(User.parse2 _, "James", "james", "555-JAMES-42")
demo(User.parse2 _, "James", "[email protected]", "555-JAMES-42")
demo(User.parse2 _, "James", "[email protected]", "555-123-4567")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment