sealed trait Validation[E, A]
case class Success[E, A](a: A) extends Validation[E, A]
case class Failure[E: Semigroup, A](e: E) extends Validation[E, A]
object Validation {
import Semigroup.SemigroupOps
type V[E] = {
type V[A] = Validation[E, A]
}
implicit def applicative[E: Semigroup]: Applicative[V[E]#V] =
new Applicative[V[E]#V] {
def pure[A](a: A): Validation[E, A] =
Success(a)
def ap[A, B](f: Validation[E, A => B])(fa: Validation[E, A]): Validation[E, B] =
match {
fa case Success(a) =>
match {
f case Success(f) => Success(f(a))
case Failure(e) => Failure(e)
}
case Failure(e) =>
match {
f case Success(f) => Failure(e)
case Failure(e2) => Failure(e <> e2)
}
}
}
}
import Applicative._
import Validation._
implicit def listSemigroup[A]: Semigroup[List[A]] =
new Semigroup[List[A]] {
def sconcat(a1: List[A], a2: List[A]): List[A] =
++ a2
a1 }
val f: Int => Int => Int =
=> y => x * y
x
def parse(x: String): Validation[List[String], Int] = // requires Scala 2.13 (33478bd)
try {
Success(x.toInt)
} catch {
case _: Exception => Failure(List("d'oh"))
}
val z1: Validation[List[String], Int] =
<%> parse("6") <*> parse("7")
f
println(z1) // Success(42)
val z2: Validation[List[String], Int] =
<%> parse("six") <*> parse("7")
f
println(z2) // Failure(List(d'oh))
This file is literate Scala, and can be run using Codedown:
$ curl \
https://earldouglas.com/posts/type-classes/applicative.md \
https://earldouglas.com/posts/type-classes/functor.md \
https://earldouglas.com/posts/type-classes/semigroup.md \
https://earldouglas.com/posts/type-classes/validation.md |
codedown scala | xargs -0 scala -nc -e
Success(42)
Failure(List(d'oh))