61
Validation with Functional Programming Sukant Hajra / @shajra April 14, 2016 Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 1 / 61

Scala Validation with Functional Programming

Embed Size (px)

Citation preview

Page 1: Scala Validation with Functional Programming

Validation with Functional Programming

Sukant Hajra / @shajra

April 14, 2016

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 1 / 61

Page 2: Scala Validation with Functional Programming

Some Goals

explore data structures for managing errorsuse type classes to get nice APIs (DSLs)see some examples with parsing

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 2 / 61

Page 3: Scala Validation with Functional Programming

scalaz.\/

Constructionscala> import scalaz.syntax.either._

import scalaz.syntax.either._

scala> 1.right[String]res0: scalaz.\/[String,Int] = \/-(1)

scala> "fail".left[Int]res1: scalaz.\/[String,Int] = -\/(fail)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 3 / 61

Page 4: Scala Validation with Functional Programming

scalaz.\/

Monadic Syntaxscala> 1.right[String].flatMap { a =>

| 2.right[String].map { b =>| a + b| }| }

res2: scalaz.\/[String,Int] = \/-(3)

scala> for {| a <- 1.right[String]| b <- 2.right[String]| } yield a + b

res3: scalaz.\/[String,Int] = \/-(3)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 4 / 61

Page 5: Scala Validation with Functional Programming

scalaz.\/

Applicative Syntaxscala> import scalaz.syntax.apply._

import scalaz.syntax.apply._

scala> 1.right[String] |@| 2.right[String] apply { _ + _ }res4: scalaz.\/[String,Int] = \/-(3)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 5 / 61

Page 6: Scala Validation with Functional Programming

scalaz.\/

Semanticsscala> (1.right[String] |@| 2.right[String]).

| apply { _ + _ }res5: scalaz.\/[String,Int] = \/-(3)

scala> (1.right[String] |@| "fail 2".left[Int]).| apply { _ + _ }

res6: scalaz.\/[String,Int] = -\/(fail 2)

scala> ("fail 1".left[Int] |@| 2.right[String]).| apply { _ + _ }

res7: scalaz.\/[String,Int] = -\/(fail 1)

scala> ("fail 1".left[Int] |@| "fail 2".left[Int]).| apply { _ + _ }

res8: scalaz.\/[String,Int] = -\/(fail 1)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 6 / 61

Page 7: Scala Validation with Functional Programming

scalaz.ValidationNel

Constructionscala> import scalaz.syntax.validation._

import scalaz.syntax.validation._

scala> 1.success[String]res9: scalaz.Validation[String,Int] = Success(1)

scala> "fail".failure[Int]res10: scalaz.Validation[String,Int] = Failure(fail)

scala> 1.successNel[String]res11: scalaz.ValidationNel[String,Int] = Success(1)

scala> "fail".failureNel[Int]res12: scalaz.ValidationNel[String,Int] =

Failure(NonEmptyList(fail))

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 7 / 61

Page 8: Scala Validation with Functional Programming

scalaz.ValidationNel

Semanticsscala> (1.successNel[String] |@| 2.successNel[String]).

| apply { _ + _ }res13: scalaz.Validation[scalaz.NonEmptyList[String],Int] =

Success(3)

scala> (1.successNel[String] |@| "fail 2".failureNel[Int]).| apply { _ + _ }

res14: scalaz.Validation[scalaz.NonEmptyList[String],Int] =Failure(NonEmptyList(fail 2))

scala> ("fail 1".failureNel[Int] |@| "fail 2".failureNel[Int]).| apply { _ + _ }

res15: scalaz.Validation[scalaz.NonEmptyList[String],Int] =Failure(NonEmptyList(fail 1, fail 2))

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 8 / 61

Page 9: Scala Validation with Functional Programming

scalaz.ValidationNel

Dangerous flatMapscala> "fail 1".failureNel[Int].flatMap { a =>

| "fail 2".failureNel[Int].map { b =>| a + b| }| }

warning: there was one deprecation warning; re-run with-deprecation for details

res16: scalaz.Validation[scalaz.NonEmptyList[String],Int] =Failure(NonEmptyList(fail 1))

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 9 / 61

Page 10: Scala Validation with Functional Programming

Composition

Defining some functionsimport scalaz.std.function._

import scalaz.syntax.compose._

def add: Int => Int => Int = a => b => a + bval mult: Int => Int => Int = a => b => a * b

Usagescala> add(2) >>> mult(10) >>> add(3) apply 0res18: Int = 23

scala> add(2) <<< mult(10) <<< add(3) apply 0res19: Int = 32

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 10 / 61

Page 11: Scala Validation with Functional Programming

What more is possible?

Consider this broken configval confText = """name = "Donald Trump"year_born = 1946nemesis {year_born = "Age of Enlightment"name = 1650

}"""

Both nemesis.year_born and nemesis.name have the wrong type.

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 11 / 61

Page 12: Scala Validation with Functional Programming

What more is possible?

Could we catch both defects with this API?case class UserData(name: String, yearBorn: Int)

def sub(key: String): ConfRead[Json] = ???def required[A : Readable](key: String): ConfRead[A] = ???

val userRead: ConfRead[UserData] =(required[String]("name") |@| required[Int]("year_born")).apply(UserData)

def fullRead: ConfRead[(UserData, UserData)] =(userRead |@| (sub("nemesis") >>> userRead)).tupled

Config.parse map fullRead.read

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 12 / 61

Page 13: Scala Validation with Functional Programming

Type Classes (a review)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 13 / 61

Page 14: Scala Validation with Functional Programming

Useful type classes

We’ll quickly review:

SemigroupComposeCovariant FunctorApplicative FunctorMonad

Also useful, but not discussed today:

ProfunctorArrow or Strong Profunctor

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 14 / 61

Page 15: Scala Validation with Functional Programming

High quality type classes

Type classes are statically dispatched interfaces that should

be implementable for many data typesprovide functions that lead to rich librarieshave only one instance per type (global uniqueness) 1

have laws (strong contracts).

Auto-deriving instances is also nice.

1Global uniqueness is debated by a fewSukant Hajra / @shajra Validation with Functional Programming April 14, 2016 15 / 61

Page 16: Scala Validation with Functional Programming

Semigroup

Definitiontrait Semigroup[A] {def append(x: A, y: A): A

}

object Semigroup {def apply[A](implicit ev: Semigroup[A]): Semigroup[A] = ev

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 16 / 61

Page 17: Scala Validation with Functional Programming

Semigroup

Lawstrait SemigroupLaws[A] extends Semigroup[A] {

def semigroupAssociativity(x: A, y: A, z: A) =append(x, append(y, z)) == append(append(x, y), z)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 17 / 61

Page 18: Scala Validation with Functional Programming

Semigroup

Syntaximplicit class SemigroupOps[A : Semigroup](a1: A) {

def |+|(a2: A): A = Semigroup[A].append(a1, a2)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 18 / 61

Page 19: Scala Validation with Functional Programming

Compose

Definitiontrait Compose[F[_, _]] {def compose[A, B, C](fbc: F[B, C], fab: F[A, B]): F[A, C]

}

object Compose {def apply[F[_, _]](implicit ev: Compose[F]): Compose[F] = ev

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 19 / 61

Page 20: Scala Validation with Functional Programming

Compose

Lawstrait ComposeLaws[F[_, _]] extends Compose[F] {

def composeAssociativity[A, B, C, D](fab: F[A, B], fbc: F[B, C], fcd: F[C, D]) =

compose(fcd, compose(fbc, fab)) ==compose(compose(fcd, fbc), fab)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 20 / 61

Page 21: Scala Validation with Functional Programming

Compose

Syntaximplicit class ComposeOps[F[_, _], A, B](f1: F[A, B])(

implicit ev: Compose[F]) {

def <<<[C](f2: F[C, A]): F[C, B] = Compose[F].compose(f1, f2)

def >>>[C](f2: F[B, C]): F[A, C] = Compose[F].compose(f2, f1)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 21 / 61

Page 22: Scala Validation with Functional Programming

(Covariant) Functor

Definitiontrait Functor[F[_]] {def map[A, B](fa: F[A])(f: A => B): F[B]

}

object Functor {def apply[F[_]](implicit ev: Functor[F]): Functor[F] = ev

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 22 / 61

Page 23: Scala Validation with Functional Programming

(Covariant) Functor

Lawstrait FunctorLaws[F[_]] extends Functor[F] {

def functorIdentity[A](fa: F[A]) =map(fa)(identity[A]) == fa

def functorComposition[A, B, C](fa: F[A], f: A => B, g: B => C) =

map(map(fa)(f))(g) == map(fa)(f andThen g)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 23 / 61

Page 24: Scala Validation with Functional Programming

Functor

Syntaximplicit class FunctorOps[F[_] : Functor, A](fa: F[A]) {

def map[B](f: A => B): F[B] = Functor[F].map(fa)(f)

def fpair: F[(A, A)] = map { a => (a, a) }

def fproduct[B](f: A => B): F[(A, B)] =map { a => (a, f(a)) }

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 24 / 61

Page 25: Scala Validation with Functional Programming

Applicative (Functor)

Definitiontrait Applicative[F[_]] extends Functor[F] {

def pure[A](a: A): F[A]

def ap[A, B](fa: F[A])(fab: F[A => B]): F[B]

}

object Applicative {def apply[F[_]](implicit ev: Applicative[F]): Applicative[F] = ev

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 25 / 61

Page 26: Scala Validation with Functional Programming

Applicative (Functor)

Lawstrait ApplicativeLaws1[F[_]] extends

Applicative[F] withFunctorLaws[F] {

def applicativeIdentity[A](fa: F[A]) =ap(fa)(pure[A => A](identity)) == fa

def applicativeHomomorphism[A, B](a: A, ab: A => B) =ap(pure(a))(pure(ab)) == pure(ab(a))

def applicativeInterchange[A, B](a: A, f: F[A => B]) =ap(pure(a))(f) == ap(f)(pure { (ff: A => B) => ff(a) })

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 26 / 61

Page 27: Scala Validation with Functional Programming

Applicative (Functor)

Lawstrait ApplicativeLaws2[F[_]] extends ApplicativeLaws1[F] {

def applicativeDerivedMap[A, B](f: A => B, fa: F[A]) =map(fa)(f) == ap(fa)(pure(f))

def applicativeComposition[A, B, C](fa: F[A], fab: F[A => B], fbc: F[B => C]) =

ap(ap(fa)(fab))(fbc) ==ap(fa)(ap(fab)(map[B=>C, (A=>B)=>(A=>C)](fbc) {bc => ab => bc compose ab }))

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 27 / 61

Page 28: Scala Validation with Functional Programming

Applicative (Functor)

Syntaximplicit class ApplicativeOps1[F[_] : Applicative, A](

fa: F[A]) {

def <*>[A, B](f: F[A => B]) = Applicative[F].ap(fa)(f)

def *>[B](fb: F[B]): F[B] =Applicative[F].ap(fa)(Functor[F].map(fb)(Function.const))

def <*[B](fb: F[B]): F[A] =Applicative[F].ap(fb)(fa map Function.const)

}

implicit class ApplicativeOps2[A](a: A) {def pure[F[_] : Applicative]: F[A] = Applicative[F].pure(a)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 28 / 61

Page 29: Scala Validation with Functional Programming

Monad

Definitiontrait Monad[F[_]] extends Applicative[F] {

def bind[A, B](fa: F[A])(fab: A => F[B]): F[B]

}

object Monad {def apply[F[_]](implicit ev: Monad[F]): Monad[F] = ev

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 29 / 61

Page 30: Scala Validation with Functional Programming

Monad

Lawstrait MonadLaws[F[_]] extends Monad[F] {

def monadRightIdentity[A](fa: F[A]) =bind(fa)(pure) == fa

def monadLeftIdentity[A, B](a: A, f: A => F[B]) =bind(pure(a))(f) == f(a)

def monadAssociativity[A, B, C](fa: F[A], f: A => F[B], g: B => F[C]) =

bind(bind(fa)(f))(g) == bind(fa) { a => bind(f(a))(g) }

def monadDerivedAp[A, B](fab: F[A => B], fa: F[A]) =bind(fa) { a => map(fab) { f => f(a) } } == ap(fa)(fab)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 30 / 61

Page 31: Scala Validation with Functional Programming

Monad

Syntaximplicit class MonadOps[F[_] : Monad, A](fa: F[A]) {

def flatMap[B](f: A => F[B]): F[B] = Monad[F].bind(fa)(f)

def >>=[B](f: A => F[B]): F[B] = flatMap(f)

def >>![B](f: A => F[B]): F[A] =flatMap { a => Monad[F].map(f(a)) { b => a } }

def >>[B](fb: F[B]): F[B] = flatMap(Function.const(fb))

import scalaz.Leibniz.===

def join[B](implicit ev: A === F[B]): F[B] =Monad[F].bind(ev.subst[F](fa))(identity)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 31 / 61

Page 32: Scala Validation with Functional Programming

Data types

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 32 / 61

Page 33: Scala Validation with Functional Programming

Non-empty List

Definitionobject nel {

case class Nel[A](head: A, tail: List[A]) {

def toList: List[A] = head :: tail

override def toString: String =s"""Nel(${head}, ${tail mkString ","})"""

}

object Nel {def of[A](head: A, tail: A*): Nel[A] =new Nel(head, tail.toList)

}

}; import nel._

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 33 / 61

Page 34: Scala Validation with Functional Programming

Non-empty List

Semigroup Instanceimport scalaz.Semigroupimport scalaz.std.list._

import scalaz.syntax.semigroup._

implicit def nelSemigroup[A]: Semigroup[Nel[A]] =new Semigroup[Nel[A]] {def append(x: Nel[A], y: => Nel[A]) =Nel[A](x.head, x.tail |+| (y.head :: y.tail))

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 34 / 61

Page 35: Scala Validation with Functional Programming

Non-empty List

Monad Instanceimport scalaz.Monadimport scalaz.syntax.monad._

implicit val nelMonad: Monad[Nel] =new Monad[Nel] {def point[A](a: => A) = Nel(a, List.empty)def bind[A, B](as: Nel[A])(f: A => Nel[B]) = {val front = f(as.head)Nel(front.head,front.tail |+| (as.tail >>= (f andThen { _.toList })))

}}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 35 / 61

Page 36: Scala Validation with Functional Programming

Non-empty List

Usagescala> Nel.of(1,2,3) |+| Nel.of(4,5,6)res2: nel.Nel[Int] = Nel(1, 2,3,4,5,6)

scala> Nel.of(1,2) >>= { x => Nel.of(x, x+10) }res3: nel.Nel[Int] = Nel(1, 11,2,12)

scala> (Nel.of(1,2) |@| Nel.of(10,100)).apply { _ * _ }res4: nel.Nel[Int] = Nel(10, 100,20,200)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 36 / 61

Page 37: Scala Validation with Functional Programming

Disjunction

Definitionobject disj {

sealed trait Disj[A, B] {def fold[C](ifLeft: A => C)(ifRight: B => C) =this match {case LeftD(a) => ifLeft(a)case RightD(b) => ifRight(b)

}}

case class LeftD[A, B](a: A) extends Disj[A, B]case class RightD[A, B](b: B) extends Disj[A, B]

}; import disj._

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 37 / 61

Page 38: Scala Validation with Functional Programming

Disjunction

Syntaximplicit class DisjOps[A](a: A) {def left[B]: Disj[A, B] = LeftD(a)def right[B]: Disj[B, A] = RightD(a)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 38 / 61

Page 39: Scala Validation with Functional Programming

Disjunction

Instancesimplicit def disjMonad[E]: Monad[Disj[E, ?]] =new Monad[Disj[E, ?]] {

def point[A](a: => A) = a.right

def bind[A, B](d: Disj[E, A])(f: A => Disj[E, B]) =d.fold { _.left[B] } { f(_) }

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 39 / 61

Page 40: Scala Validation with Functional Programming

Disjunction

Usagescala> (1.right[String] |@| 2.right[String]).

| apply { _ + _ }res5: disj.Disj[String,Int] = RightD(3)

scala> ("fail 1".left[Int] |@| "fail 2".left[Int]).| apply { _ + _ }

res6: disj.Disj[String,Int] = LeftD(fail 1)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 40 / 61

Page 41: Scala Validation with Functional Programming

Validation

Definitionobject checked {

sealed trait Checked[A, B] {def fold[C](ifFail: A => C)(ifPass: B => C) =this match {case Fail(a) => ifFail(a)case Pass(b) => ifPass(b)

}}

case class Fail[A, B](a: A) extends Checked[A, B]case class Pass[A, B](b: B) extends Checked[A, B]

}; import checked._

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 41 / 61

Page 42: Scala Validation with Functional Programming

Validation

Syntaximplicit class CheckedOps1[A](a: A) {def pass[B]: Checked[B, A] = Pass(a)def fail[B]: Checked[A, B] = Fail(a)def passNel[B]: Checked[Nel[B], A] = Pass(a)def failNel[B]: Checked[Nel[A], B] = Fail(Nel of a)

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 42 / 61

Page 43: Scala Validation with Functional Programming

Validation

Instancesimport scalaz.Applicative

implicit def checkedApplicative[E : Semigroup]: Applicative[Checked[E, ?]] =

new Applicative[Checked[E, ?]] {

def point[A](a: => A) = a.pass[E]

def ap[A, B](c: => Checked[E, A])(f: => Checked[E, A=>B]) =c.fold { e1 =>f.fold(e2 => (e1 |+| e2).fail[B])(_ => e1.fail[B])

} { a =>f.fold(_.fail[B])(_.apply(a).pass[E])

}

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 43 / 61

Page 44: Scala Validation with Functional Programming

Validation

Usagescala> (1.passNel[String] |@| 2.passNel[String]).

| apply { _ + _ }res8: checked.Checked[nel.Nel[String],Int] = Pass(3)

scala> ("fail 1".failNel[Int] |@| "fail 2".failNel[Int]).| apply { _ + _ }

res9: checked.Checked[nel.Nel[String],Int] = Fail(Nel(fail 2,fail 1))

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 44 / 61

Page 45: Scala Validation with Functional Programming

Read

Definitioncase class Read[F[_], A, B](read: A => F[B])

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 45 / 61

Page 46: Scala Validation with Functional Programming

Read

Compose Instanceimport scalaz.Compose

implicit def readCompose[F[_] : Monad]: Compose[Read[F, ?, ?]] =

new Compose[Read[F, ?, ?]] {def compose[A, B, C](f: Read[F, B, C], g: Read[F, A, B]) =Read { a => g.read(a) >>= f.read }

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 46 / 61

Page 47: Scala Validation with Functional Programming

Read

Applicative Instanceimplicit def readApplicative[F[_] : Applicative, A]

: Applicative[Read[F, A, ?]] =new Applicative[Read[F, A, ?]] {def point[B](b: => B) = Read { a => b.pure[F] }def ap[B, C](r: => Read[F, A, B])(f: => Read[F, A, B=>C]) =Read { a => (f.read(a) |@| r.read(a)) { _ apply _ } }

}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 47 / 61

Page 48: Scala Validation with Functional Programming

ReadC

Definitioncase class ReadC[A, E, B](read: A => Checked[E, B])

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 48 / 61

Page 49: Scala Validation with Functional Programming

ReadC

Instancesimplicit def readCCompose[E]: Compose[ReadC[?, E, ?]] =new Compose[ReadC[?, E, ?]] {def compose[A, B, C]

(f: ReadC[B, E, C], g: ReadC[A, E, B]) =ReadC { a => g.read(a).fold(_.fail[C])(f.read) }

}

implicit def readCApplicative[A, E : Semigroup]: Applicative[ReadC[A, E, ?]] =

new Applicative[ReadC[A, E, ?]] {def point[B](b: => B) =ReadC { a => b.pure[Checked[E, ?]] }

def ap[B, C](r: => ReadC[A, E, B])(f: => ReadC[A, E, B=>C]) =

ReadC { a => (f.read(a) |@| r.read(a)) { _ apply _ } }}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 49 / 61

Page 50: Scala Validation with Functional Programming

Parsing Example

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 50 / 61

Page 51: Scala Validation with Functional Programming

Knobs library extension

Tracking path traversedimport scalaz.Showimport scalaz.std.string._

import scalaz.syntax.foldable._

case class Path(elems: List[String]) {

def -\(elem: String) = new Path(elem.trim +: elems)

override def toString = elems.reverse intercalate "."

}

def emptyPath: Path = new Path(List.empty)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 51 / 61

Page 52: Scala Validation with Functional Programming

Knobs library extension

Path-Config pairimport knobs.Config

case class Knobs(path: Path, config: Config) {def -\(elem: String): Knobs =Knobs(path -\ elem, config subconfig elem)

}

def rootKnobs(config: Config): Knobs =Knobs(emptyPath, config)

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 52 / 61

Page 53: Scala Validation with Functional Programming

Knobs library extension

Faultsimport scala.reflect.runtime.universe.{ typeTag, TypeTag }

sealed abstract class ParseFault { def path: Path }

final case class KeyNotFound(path: Path) extends ParseFault

final case class WrongType(path: Path, typetag: TypeTag[_])extends ParseFault

def keyNotFound(path: Path): ParseFault =KeyNotFound(path)

def wrongType[A : TypeTag](path: Path): ParseFault =WrongType(path, typeTag[A])

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 53 / 61

Page 54: Scala Validation with Functional Programming

Knobs library extension

Type aliastype Parse[A, B] = ReadC[A, Nel[ParseFault], B]type KnobsRead[A] = Parse[Knobs, A]

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 54 / 61

Page 55: Scala Validation with Functional Programming

Knobs library extension

Read subconfigdef sub(path: String): KnobsRead[Knobs] =ReadC { _ -\ path passNel }

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 55 / 61

Page 56: Scala Validation with Functional Programming

Knobs library extension

Lookup configurationimport knobs.{ CfgValue, Configured }

def required[A : Configured : TypeTag](path: String):KnobsRead[A] =

ReadC { conf =>val reportedPath = conf.path -\ pathconf.config.lookup[CfgValue](path).fold(keyNotFound(reportedPath).fail[CfgValue])(_.pass).fold(_.failNel[A]) { v =>v.convertTo[A].fold(wrongType[A](reportedPath).failNel[A])(_.passNel)

}}

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 56 / 61

Page 57: Scala Validation with Functional Programming

Knobs library extension

Config readerimport scalaz.syntax.compose._

case class UserData(name: String, yearBorn: Int)

val userRead: KnobsRead[UserData] =(required[String]("name") |@| required[Int]("year_born")).apply(UserData)

def fullRead: KnobsRead[(UserData, UserData)] =(userRead |@| (sub("nemesis") >>> userRead)).tupled

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 57 / 61

Page 58: Scala Validation with Functional Programming

Knobs library extension

Broken config exampleval confText = """name = "Donald Trump"year_born = 1946nemesis {year_born = "Age of Enlightment"name = 1650

}"""

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 58 / 61

Page 59: Scala Validation with Functional Programming

Knobs library extension

Config readerscala> Config.parse(confText).map { conf =>

| fullRead read rootKnobs(conf)| }

res25:scalaz.\/[Throwable,checked.Checked[nel.Nel[ParseFault],(UserData,UserData)]] =\/-(Fail(Nel(WrongType(nemesis.year_born,TypeTag[Int]),WrongType(nemesis.name,TypeTag[String]))))

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 59 / 61

Page 60: Scala Validation with Functional Programming

Wrapping up

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 60 / 61

Page 61: Scala Validation with Functional Programming

This material

is compiler-checked (half-literate programming) using:I Rob Norris’s tut SBT pluginI pandocI LATEXI Beamer

is at https://github.com/shajra/shajra-presentations.

Sukant Hajra / @shajra Validation with Functional Programming April 14, 2016 61 / 61