Validation

Implementation

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] =
        fa match {
          case Success(a) =>
            f match {
              case Success(f) => Success(f(a))
              case Failure(e) => Failure(e)
            }
          case Failure(e) =>
            f match {
              case Success(f)  => Failure(e)
              case Failure(e2) => Failure(e <> e2)
            }
        }
    }
}

Dependencies

Example

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] =
      a1 ++ a2
  }

val f: Int => Int => Int =
  x => y => x * y

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] =
  f <%> parse("6") <*> parse("7")

println(z1) // Success(42)

val z2: Validation[List[String], Int] =
  f <%> parse("six") <*> parse("7")

println(z2) // Failure(List(d'oh))

References

Demo

This file is literate Scala, and can be run using Codedown:

$ curl -s \
    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))