Validation

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

// TODO: Unnecessary with Scala 2.13.0-M5 (33478bd)
type VLA[A] = Validation[List[String], A]

def parse(x: String): VLA[Int] =
  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))