Slide 1

Slide 1 text

Scalaz There’s an applicative in my functor! Photo Credit: http://www.flickr.com/photos/yannconz/2796312310

Slide 2

Slide 2 text

Agenda • Option • Monoid • Applicative • Validation • Lens

Slide 3

Slide 3 text

Options • Option functions & typeclass instances • Option syntax extensions • Using Option with other parts of Scalaz

Slide 4

Slide 4 text

Constructing Options Some(42) Some[Int] None None

Slide 5

Slide 5 text

Constructing Options Some(42) Some[Int] None None import scalaz.std.option._ some(42) Option[Int] none[Int] Option[Int]

Slide 6

Slide 6 text

Constructing Options Some(42) Some[Int] None None import scalaz.std.option._ some(42) Option[Int] none[Int] Option[Int] Imports functions & typeclass instances for option

Slide 7

Slide 7 text

Constructing Options val os = List(Some(42), None, Some(20)) os.foldLeft(None) { (acc, o) => acc orElse o }

Slide 8

Slide 8 text

Constructing Options val os = List(Some(42), None, Some(20)) os.foldLeft(None) { (acc, o) => acc orElse o } error: type mismatch; found : Option[Int] required: None.type os.foldLeft(None) { (acc, o) => acc orElse o } ^

Slide 9

Slide 9 text

Constructing Options val os = List(Some(42), None, Some(20)) os.foldLeft(None) { (acc, o) => acc orElse o } os.foldLeft(None: Option[Int]) { (acc, o) => acc orElse o } error: type mismatch; found : Option[Int] required: None.type os.foldLeft(None) { (acc, o) => acc orElse o } ^ Some(42)

Slide 10

Slide 10 text

Constructing Options val os = List(Some(42), None, Some(20)) os.foldLeft(None) { (acc, o) => acc orElse o } os.foldLeft(None: Option[Int]) { (acc, o) => acc orElse o } error: type mismatch; found : Option[Int] required: None.type os.foldLeft(None) { (acc, o) => acc orElse o } ^ os.foldLeft(none[Int]) { (acc, o) => acc orElse o } Some(42) Some(42)

Slide 11

Slide 11 text

Option conditional import scalaz.syntax.std.option._ def xget(opt: Option[Int]) = opt | Int.MaxValue xget(Some(42)) xget(None)

Slide 12

Slide 12 text

Option conditional import scalaz.syntax.std.option._ def xget(opt: Option[Int]) = opt | Int.MaxValue xget(Some(42)) xget(None) Imports syntax extensions for option

Slide 13

Slide 13 text

Option conditional import scalaz.syntax.std.option._ def xget(opt: Option[Int]) = opt | Int.MaxValue xget(Some(42)) xget(None) Alias for getOrElse

Slide 14

Slide 14 text

Option conditional import scalaz.syntax.std.option._ def xget(opt: Option[Int]) = opt | Int.MaxValue xget(Some(42)) xget(None) 42 2147483647

Slide 15

Slide 15 text

Option unary ~ import scalaz.std.anyVal._ import scalaz.std.option._ import scalaz.syntax.std.option._ def zget(opt: Option[Int]) = ~opt zget(Some(42)) zget(None)

Slide 16

Slide 16 text

Option unary ~ import scalaz.std.anyVal._ import scalaz.std.option._ import scalaz.syntax.std.option._ def zget(opt: Option[Int]) = ~opt zget(Some(42)) zget(None) Imports functions & typeclass instances for int, float, etc.

Slide 17

Slide 17 text

Option unary ~ import scalaz.std.anyVal._ import scalaz.std.option._ import scalaz.syntax.std.option._ def zget(opt: Option[Int]) = ~opt zget(Some(42)) zget(None) Alias for getOrElse(zero)

Slide 18

Slide 18 text

Option unary ~ import scalaz.std.anyVal._ import scalaz.std.option._ import scalaz.syntax.std.option._ def zget(opt: Option[Int]) = ~opt zget(Some(42)) zget(None) Alias for getOrElse(zero) 0 42

Slide 19

Slide 19 text

Option unary ~ import scalaz.std.anyVal._ import scalaz.std.option._ import scalaz.syntax.std.option._ def zget(opt: Option[Int]) = ~opt zget(Some(42)) zget(None) Alias for getOrElse(zero) 0 42 Where does zero come from?

Slide 20

Slide 20 text

Option unary ~ import scalaz.Monoid import scalaz.std.string._ import scalaz.std.list._ def mzget[A : Monoid](opt: Option[A]) = ~opt mzget(none[Int]) mzget(none[String]) mzget(none[List[Int]]) 0 “” List()

Slide 21

Slide 21 text

Aside: Context Bounds def mzget[A : Monoid](opt: Option[A]) = ~opt

Slide 22

Slide 22 text

Aside: Context Bounds def mzget[A : Monoid](opt: Option[A]) = ~opt • Monoid is a context bound for type A

Slide 23

Slide 23 text

Aside: Context Bounds def mzget[A : Monoid](opt: Option[A]) = ~opt • Monoid is a context bound for type A • Means there must be an implicit value of type Monoid[A] in implicit scope

Slide 24

Slide 24 text

Aside: Context Bounds def mzget[A : Monoid](opt: Option[A]) = ~opt • Monoid is a context bound for type A • Means there must be an implicit value of type Monoid[A] in implicit scope • Equivalent to: def mzget[A](opt: Option[A])( implicit m: Monoid[A]) = ~opt

Slide 25

Slide 25 text

Aside: Context Bounds def mzget[A : Monoid](opt: Option[A]) = ~opt • Monoid is a context bound for type A • Means there must be an implicit value of type Monoid[A] in implicit scope • Equivalent to: def mzget[A](opt: Option[A])( implicit m: Monoid[A]) = ~opt • Even supports multiple bounds: def other[A : Monoid : Functor] = ...

Slide 26

Slide 26 text

Option err def knowBetter[A](opt: Option[A]) = opt err "You promised!" knowBetter(some(42)) knowBetter(none)

Slide 27

Slide 27 text

Option err def knowBetter[A](opt: Option[A]) = opt err "You promised!" knowBetter(some(42)) knowBetter(none) Alias for getOrElse(sys.err(msg)) 42 throws RuntimException(“You promised!”) Good alternative to opt.get when you know it’s defined!

Slide 28

Slide 28 text

Option fold/cata import java.net.InetAddress def ipAddress(opt: Option[InetAddress]) = opt.fold(_.getHostAddress, "0.0.0.0") ipAddress(Some(InetAddress.getLocalHost)) ipAddress(None)

Slide 29

Slide 29 text

Option fold/cata import java.net.InetAddress def ipAddress(opt: Option[InetAddress]) = opt.fold(_.getHostAddress, "0.0.0.0") ipAddress(Some(InetAddress.getLocalHost)) ipAddress(None) 10.0.1.12 0.0.0.0 Replacement for matching on Some/None

Slide 30

Slide 30 text

Option ifNone var cnt = 0 some(42) ifNone { cnt += 1 } none ifNone { cnt += 1 } println(cnt)

Slide 31

Slide 31 text

Concatenate Given a list of options, how do you sum them? List(some(42), none, some(51)).sum error: could not find implicit value for parameter num: Numeric[Option[Int]]

Slide 32

Slide 32 text

Concatenate Given a list of options, how do you sum them? List(some(42), none, some(51)).sum error: could not find implicit value for parameter num: Numeric[Option[Int]] import scalaz.syntax.traverse._ List(some(42), none, some(51)).concatenate 93

Slide 33

Slide 33 text

Sequence Given a list of options, how do you get option of list? List(some(42), none, some(51)).sequence List(some(42), some(51)).sequence None Some(List(42, 51))

Slide 34

Slide 34 text

Semigroup & Monoid Review • Semigroup append: A 㱺 A 㱺 A • Monoid extends Semigroup zero: A Not Scala syntax!

Slide 35

Slide 35 text

import scalaz.std.anyVal._ import scalaz.syntax.monoid._ 1 |+| 2 import scalaz.std.list._ List(1, 2) |+| List(3, 4) Syntax 3 List(1, 2, 3, 4)

Slide 36

Slide 36 text

import scalaz.std.map._ import scalaz.std.set._ val low = Map( 'odd -> Set(1, 3), 'even -> Set(2, 4)) val hi = Map( 'odd -> Set(5, 7), 'even -> Set(6, 8)) low |+| hi Syntax

Slide 37

Slide 37 text

import scalaz.std.map._ import scalaz.std.set._ val low = Map( 'odd -> Set(1, 3), 'even -> Set(2, 4)) val hi = Map( 'odd -> Set(5, 7), 'even -> Set(6, 8)) low |+| hi Syntax Map(‘odd -> Set(1, 3, 5, 7), ‘even -> Set(2, 4, 6, 8))

Slide 38

Slide 38 text

Option is Monoid • There’s a monoid of Option[A] if A is a Semigroup • zero = None • append = ???

Slide 39

Slide 39 text

Option is Monoid implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = new Monoid[Option[A]] { def append(f1: Option[A], f2: => Option[A]) = (f1, f2) match { case (Some(a1), Some(a2)) => Some(Semigroup[A].append(a1, a2)) case (Some(a1), None) => f1 case (None, Some(a2)) => f2 case (None, None) => None } def zero: Option[A] = None } From Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/std/Option.scala

Slide 40

Slide 40 text

Option is Monoid import scalaz.syntax.monoid._ some(20) |+| none |+| some(22) Some(42)

Slide 41

Slide 41 text

Option alternative monoids • Can we find other monoids for Option? • Must implement zero and append • But remember the monoid laws: • Closure • Associativity • Identity See: http://en.wikipedia.org/wiki/Monoid

Slide 42

Slide 42 text

Option alternative monoids • Keep everything the same as semigroup based monoid but focus on: case (Some(a1), Some(a2)) => • Left case (Some(a1), Some(a2)) => f1 • Right case (Some(a1), Some(a2)) => f2

Slide 43

Slide 43 text

some(20).first |+| none.first |+| some(22).first some(20).last |+| none.last |+| some(22).last Option alternative monoids Some(20) Some(22)

Slide 44

Slide 44 text

Functor & Monad Review • Functor map: F[A] 㱺 (A 㱺 B) 㱺 F[B] • Monad extends Functor point: A 㱺 M[A] flatMap: M[A] 㱺 (A 㱺 M[B]) 㱺 M[B] Not Scala syntax!

Slide 45

Slide 45 text

Functor & Monad Review • Functor map: F[A] 㱺 (A 㱺 B) 㱺 F[B] • Monad extends Functor point: A 㱺 M[A] flatMap: M[A] 㱺 (A 㱺 M[B]) 㱺 M[B] Not Scala syntax! Option[Int] 㱺 (Int 㱺 String) 㱺 Option[String]

Slide 46

Slide 46 text

Functor & Monad Review • Functor map: F[A] 㱺 (A 㱺 B) 㱺 F[B] • Monad extends Functor point: A 㱺 M[A] flatMap: M[A] 㱺 (A 㱺 M[B]) 㱺 M[B] Not Scala syntax! Option[Int] 㱺 (Int 㱺 String) 㱺 Option[String]

Slide 47

Slide 47 text

Functor & Monad Review • Functor map: F[A] 㱺 (A 㱺 B) 㱺 F[B] • Monad extends Functor point: A 㱺 M[A] flatMap: M[A] 㱺 (A 㱺 M[B]) 㱺 M[B] Not Scala syntax! Option[Int] 㱺 (Int 㱺 String) 㱺 Option[String] Option[String] 㱺 (String 㱺 Option[Int]) 㱺 Option[Int] String 㱺 Option[String]

Slide 48

Slide 48 text

What’s this? • ??? extends Functor point: A 㱺 F[A] ???: F[A] 㱺 F[A 㱺 B] 㱺 F[B]

Slide 49

Slide 49 text

What’s this? • ??? extends Functor point: A 㱺 F[A] ???: F[A] 㱺 F[A 㱺 B] 㱺 F[B] • Read as... Given a function from A to B in a context F and a value of A in a context F, produce a value of B in context F.

Slide 50

Slide 50 text

What’s this? • ??? extends Functor point: A 㱺 F[A] ???: F[A] 㱺 F[A 㱺 B] 㱺 F[B] • Read as... Given a function from A to B in a context F and a value of A in a context F, produce a value of B in context F. • Represents function application in a context

Slide 51

Slide 51 text

What’s this? • ??? extends Functor point: A 㱺 F[A] ???: F[A] 㱺 F[A 㱺 B] 㱺 F[B] • Read as... Given a function from A to B in a context F and a value of A in a context F, produce a value of B in context F. • Represents function application in a context • Defined in http://www.soi.city.ac.uk/~ross/papers/Applicative.html

Slide 52

Slide 52 text

Applicative • Applicative extends Functor point: A 㱺 F[A] ap: F[A] 㱺 F[A 㱺 B] 㱺 F[B] Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Pointed.scala https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Applicative.scala

Slide 53

Slide 53 text

Applicative • Applicative extends Functor point: A 㱺 F[A] ap: F[A] 㱺 F[A 㱺 B] 㱺 F[B] • Or in proper Scala: trait Pointed[F[_]] extends Functor[F] { def point[A](a: => A): F[A] } trait Applicative[F[_]] extends Functor[F] with Pointed[F] { def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B] } Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Pointed.scala https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Applicative.scala

Slide 54

Slide 54 text

implicit val optionInstance = new Applicative[Option] { override def point[A](a: => A) = ??? override def ap[A, B]( fa: => Option[A])(f: => Option[A => B]) = ??? } Option Is Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala Let’s define an applicative instance for Option

Slide 55

Slide 55 text

implicit val optionInstance = new Applicative[Option] { override def point[A](a: => A) = ??? override def ap[A, B]( fa: => Option[A])(f: => Option[A => B]) = ??? } Option Is Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala How can we lift a value of A to Option[A]?

Slide 56

Slide 56 text

implicit val optionInstance = new Applicative[Option] { override def point[A](a: => A) = Some(a) override def ap[A, B]( fa: => Option[A])(f: => Option[A => B]) = ??? } Option Is Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala

Slide 57

Slide 57 text

implicit val optionInstance = new Applicative[Option] { override def point[A](a: => A) = Some(a) override def ap[A, B]( fa: => Option[A])(f: => Option[A => B]) = ??? } Option Is Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala How can we apply the function in Option[A=>B] to Option[A]?

Slide 58

Slide 58 text

implicit val optionInstance = new Applicative[Option] { override def point[A](a: => A) = Some(a) override def ap[A, B]( fa: => Option[A])(f: => Option[A => B]) = f match { case Some(f) => fa match { case Some(x) => Some(f(x)) case None => None } case None => None } } Option Is Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala

Slide 59

Slide 59 text

implicit val optionInstance = new Applicative[Option] { override def point[A](a: => A) = Some(a) override def ap[A, B]( fa: => Option[A])(f: => Option[A => B]) = f match { case Some(f) => fa match { case Some(x) => Some(f(x)) case None => None } case None => None } } Option Is Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala What about map[A](f: A => B): Option[B]?

Slide 60

Slide 60 text

trait Applicative[F[_]] extends Functor[F] with Pointed[F] { def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B] override def map[A, B](fa: F[A])(f: A => B): F[B] = ??? } Functor.map on Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala Can we define map in terms of pointed and ap?

Slide 61

Slide 61 text

trait Applicative[F[_]] extends Functor[F] with Pointed[F] { def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B] override def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(point(f)) } Functor.map on Applicative Summarized from Scalaz Seven source https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Option.scala

Slide 62

Slide 62 text

import scalaz.std.option._ import scalaz.syntax.applicative._ val oa5: Option[Int => Int] = Some((_: Int) + 5) some(15) <*> oa5 Applicative Usage

Slide 63

Slide 63 text

import scalaz.std.option._ import scalaz.syntax.applicative._ val oa5: Option[Int => Int] = Some((_: Int) + 5) some(15) <*> oa5 Applicative Usage Alias for ap

Slide 64

Slide 64 text

import scalaz.std.option._ import scalaz.syntax.applicative._ val oa5: Option[Int => Int] = Some((_: Int) + 5) some(15) <*> oa5 Applicative Usage Alias for ap Some(20)

Slide 65

Slide 65 text

import scalaz.std.option._ import scalaz.syntax.applicative._ some(15).<**>(some(5)) { _ + _ } some(15).<***>(some(5), some(6)) { _ + _ + _ } some(15).<****>(some(5), some(6), some(7)) { _ + _ + _ + _ } Applicative Usage

Slide 66

Slide 66 text

import scalaz.std.option._ import scalaz.syntax.applicative._ some(15).<**>(some(5)) { _ + _ } some(15).<***>(some(5), some(6)) { _ + _ + _ } some(15).<****>(some(5), some(6), some(7)) { _ + _ + _ + _ } Applicative Usage Some(20) Some(26) Some(33)

Slide 67

Slide 67 text

import scalaz.std.option._ import scalaz.syntax.applicative._ (some(15) |@| some(5)) apply { _ + _ } (some(15) |@| some(5) |@| some(6)) apply { _ + _ + _ } (some(15) |@| some(5)).tupled Applicative Builder Most common and convenient usage

Slide 68

Slide 68 text

import scalaz.std.option._ import scalaz.syntax.applicative._ (some(15) |@| some(5)) apply { _ + _ } (some(15) |@| some(5) |@| some(6)) apply { _ + _ + _ } (some(15) |@| some(5)).tupled Applicative Builder Some(20) Some(26) Some((15, 5)) Most common and convenient usage

Slide 69

Slide 69 text

import scalaz.std.option._ import scalaz.syntax.applicative._ (some(15) |@| some(5)) apply { _ + _ } (some(15) |@| some(5) |@| some(6)) apply { _ + _ + _ } (some(15) |@| some(5)).tupled Applicative Builder Supports up to 12 arguments Some(20) Some(26) Some((15, 5)) Most common and convenient usage

Slide 70

Slide 70 text

import scalaz.std.option._ import scalaz.syntax.applicative._ case class Album(name: String, artist: String) (some("Wilco") ⊛ some("Sky Blue Sky")) apply Album.apply Applicative Builder

Slide 71

Slide 71 text

import scalaz.std.option._ import scalaz.syntax.applicative._ case class Album(name: String, artist: String) (some("Wilco") ⊛ some("Sky Blue Sky")) apply Album.apply Applicative Builder Companion has an apply method automatically created

Slide 72

Slide 72 text

import scalaz.std.option._ import scalaz.syntax.applicative._ case class Album(name: String, artist: String) (some("Wilco") ⊛ some("Sky Blue Sky")) apply Album.apply Applicative Builder Companion has an apply method automatically created Alias for |@|

Slide 73

Slide 73 text

import scalaz.std.option._ import scalaz.syntax.applicative._ case class Album(name: String, artist: String) (some("Wilco") ⊛ some("Sky Blue Sky")) apply Album.apply Applicative Builder Some(Album(Wilco, Sky Blue Sky)) Companion has an apply method automatically created Alias for |@|

Slide 74

Slide 74 text

Why Applicatives? • Less restrictive than Monads, and thus, more general (powerful) • Composable http://blog.tmorris.net/monads-do-not-compose/ • Support parallel computation More on this point later...

Slide 75

Slide 75 text

A word on typeclasses • So far we’ve seen Semigroup, Monoid, Functor, Pointed, Applicative, Monad

Slide 76

Slide 76 text

A word on typeclasses • So far we’ve seen Semigroup, Monoid, Functor, Pointed, Applicative, Monad • There are many others -- ApplicativePlus, MonadPlus, Comonad, Category, Arrow, ArrowPlus, Foldable, Traversable, Monad Transformers, Reader, Writer, State, Identity, and more

Slide 77

Slide 77 text

A word on typeclasses • So far we’ve seen Semigroup, Monoid, Functor, Pointed, Applicative, Monad • There are many others -- ApplicativePlus, MonadPlus, Comonad, Category, Arrow, ArrowPlus, Foldable, Traversable, Monad Transformers, Reader, Writer, State, Identity, and more • These are functional design patterns • Simpler than most OO design patterns

Slide 78

Slide 78 text

Some Haskell Typeclasses Credit: Typeclassopedia http://www.haskell.org/haskellwiki/Typeclassopedia#Introduction

Slide 79

Slide 79 text

Typeclass References • Haskell Typeclassopedia is a great reference for learning these http://www.haskell.org/haskellwiki/Typeclassopedia • Scala Typeclassopedia describes the big 3 (Functor, Applicative, Monad) http://typeclassopedia.bitbucket.org/

Slide 80

Slide 80 text

sealed trait Validation[+E, +A] { ... } final case class Success[E, A](a: A) extends Validation[E, A] final case class Failure[E, A](e: E) extends Validation[E, A] Validation

Slide 81

Slide 81 text

Validation • Represents either success or failure • On success, provides the successful value • On failure, provides the failure value • Sound familiar?

Slide 82

Slide 82 text

Isomorphic to Either ≅

Slide 83

Slide 83 text

Isomorphic to Either Validation Either Success Right Failure Left ≅

Slide 84

Slide 84 text

Isomorphic to Either Validation Either Success Right Failure Left ≅ Q: So why not just use Either?

Slide 85

Slide 85 text

Isomorphic to Either Validation Either Success Right Failure Left ≅ Q: So why not just use Either? A: When Validation is constructed with an error type that has a Semigroup, there exists an Applicative Functor for Validation that accumulates errors

Slide 86

Slide 86 text

import scalaz.{Success, Failure} Success(42) Failure("boom") Constructing Validations Success[Nothing, Int] Failure[String, Nothing]

Slide 87

Slide 87 text

import scalaz.{Success, Failure} Success(42) Failure("boom") Constructing Validations Success[Nothing, Int] Failure[String, Nothing] import scalaz.Validation Validation.success(42) Validation.failure("boom") Validation[Nothing, Int] Validation[String, Nothing]

Slide 88

Slide 88 text

import scalaz.{Success, Failure} Success(42) Failure("boom") Constructing Validations Success[Nothing, Int] Failure[String, Nothing] import scalaz.Validation Validation.success(42) Validation.failure("boom") Validation[Nothing, Int] Validation[String, Nothing] import scalaz.syntax.validation._ 42.success "boom".fail Validation[Nothing, Int] Validation[String, Nothing]

Slide 89

Slide 89 text

import scalaz.{Success, Failure} Success(42) Failure("boom") Constructing Validations Success[Nothing, Int] Failure[String, Nothing] import scalaz.Validation Validation.success(42) Validation.failure("boom") Validation[Nothing, Int] Validation[String, Nothing] import scalaz.syntax.validation._ 42.success "boom".fail Validation[Nothing, Int] Validation[String, Nothing] 42.success[String] "boom".fail[Int] Validation[String, Int] Validation[String, Int]

Slide 90

Slide 90 text

import scalaz.std.option._ import scalaz.syntax.std.option._ some(42).toSuccess("boom") none[Int].toSuccess("boom") some(42).toFailure("boom") none[Int].toFailure("boom") Option to Validation

Slide 91

Slide 91 text

import scalaz.std.option._ import scalaz.syntax.std.option._ some(42).toSuccess("boom") none[Int].toSuccess("boom") some(42).toFailure("boom") none[Int].toFailure("boom") Option to Validation Success(42) Failure(“boom”)

Slide 92

Slide 92 text

import scalaz.std.option._ import scalaz.syntax.std.option._ some(42).toSuccess("boom") none[Int].toSuccess("boom") some(42).toFailure("boom") none[Int].toFailure("boom") Option to Validation Success(42) Failure(“boom”) Failure(42) Success(“boom”)

Slide 93

Slide 93 text

import scalaz.syntax.id._ import scalaz.Validation.fromEither fromEither(42.right) fromEither("boom".left) Either to Validation

Slide 94

Slide 94 text

import scalaz.syntax.id._ import scalaz.Validation.fromEither fromEither(42.right) fromEither("boom".left) Either to Validation Success(42) Failure(“boom”)

Slide 95

Slide 95 text

import scalaz.Validation.fromTryCatch fromTryCatch("42".toInt) fromTryCatch("notAnInt".toInt) Throwing to Validation

Slide 96

Slide 96 text

import scalaz.Validation.fromTryCatch fromTryCatch("42".toInt) fromTryCatch("notAnInt".toInt) Throwing to Validation Success(42) Failure(java.lang.NumberFormatException: For input string: "notAnInt")

Slide 97

Slide 97 text

import scalaz.Validation.fromTryCatch fromTryCatch("42".toInt) fromTryCatch("notAnInt".toInt) Throwing to Validation Success(42) Failure(java.lang.NumberFormatException: For input string: "notAnInt") fromTryCatch("notAnInt".toInt) .fail.map { _.getMessage }.validation

Slide 98

Slide 98 text

import scalaz.Validation.fromTryCatch fromTryCatch("42".toInt) fromTryCatch("notAnInt".toInt) Throwing to Validation Success(42) Failure(java.lang.NumberFormatException: For input string: "notAnInt") fromTryCatch("notAnInt".toInt) .fail.map { _.getMessage }.validation Failure(For input string: "notAnInt")

Slide 99

Slide 99 text

import scalaz.syntax.bifunctor._ fromTryCatch("notAnInt".toInt). <-: { _.getMessage } Mapping via Bifunctor Bifunctor = functor of 2 arguments <-: = Map left value

Slide 100

Slide 100 text

import scalaz.syntax.bifunctor._ fromTryCatch("notAnInt".toInt). <-: { _.getMessage } Mapping via Bifunctor Failure(For input string: "notAnInt") Bifunctor = functor of 2 arguments <-: = Map left value

Slide 101

Slide 101 text

import scalaz.syntax.bifunctor._ fromTryCatch("notAnInt".toInt). <-: { _.getMessage } Mapping via Bifunctor fromTryCatch("notAnInt".toInt). bimap(_.getMessage, identity) Failure(For input string: "notAnInt") Bifunctor = functor of 2 arguments <-: = Map left value

Slide 102

Slide 102 text

import scalaz.syntax.bifunctor._ fromTryCatch("notAnInt".toInt). <-: { _.getMessage } Mapping via Bifunctor fromTryCatch("notAnInt".toInt). bimap(_.getMessage, identity) Failure(For input string: "notAnInt") Failure(For input string: "notAnInt") Bifunctor = functor of 2 arguments <-: = Map left value

Slide 103

Slide 103 text

Validation is Monad def justMessage[S]( v: Validation[Throwable, S]): Validation[String, S] = v.<-: { _.getMessage } def extractId( metadata: Map[String, String]): Validation[String, UUID] = for { str <- metadata.get("id").toSuccess("No id property") id <- justMessage(fromTryCatch(UUID.fromString(str))) } yield id extractId(Map.empty) extractId(Map("id" -> "notUuid")) extractId(Map("id" -> UUID.randomUUID.toString))

Slide 104

Slide 104 text

Validation is Monad def justMessage[S]( v: Validation[Throwable, S]): Validation[String, S] = v.<-: { _.getMessage } def extractId( metadata: Map[String, String]): Validation[String, UUID] = for { str <- metadata.get("id").toSuccess("No id property") id <- justMessage(fromTryCatch(UUID.fromString(str))) } yield id extractId(Map.empty) extractId(Map("id" -> "notUuid")) extractId(Map("id" -> UUID.randomUUID.toString)) Failure(No id property) Failure(Invalid UUID string: notUuid) Success(f6af15db-feeb-4895-8c67-a70a6f947db9)

Slide 105

Slide 105 text

Aside: Monadic DSL def justMessage[S]( v: Validation[Throwable, S]): Validation[String, S] = v.<-: { _.getMessage } def extractId( metadata: Map[String, String]): Validation[String, UUID] = for { str <- metadata.get("id").toSuccess("No id property") id <- justMessage(fromTryCatch(UUID.fromString(str))) } yield id extractId(Map.empty) extractId(Map("id" -> "notUuid")) extractId(Map("id" -> UUID.randomUUID.toString)) This reads pretty naturally!

Slide 106

Slide 106 text

Aside: Monadic DSL def justMessage[S]( v: Validation[Throwable, S]): Validation[String, S] = v.<-: { _.getMessage } def extractId( metadata: Map[String, String]): Validation[String, UUID] = for { str <- metadata.get("id").toSuccess("No id property") id <- justMessage(fromTryCatch(UUID.fromString(str))) } yield id extractId(Map.empty) extractId(Map("id" -> "notUuid")) extractId(Map("id" -> UUID.randomUUID.toString)) This doesn’t! Notation is inside-out!

Slide 107

Slide 107 text

Aside: Monadic DSL import scalaz.syntax.id._ def justMessage[S]( v: Validation[Throwable, S]): Validation[String, S] = v.<-: { _.getMessage } def parseUUID(s: String): Validation[Throwable, UUID] = fromTryCatch(UUID.fromString(s)) def extractId( metadata: Map[String, String]): Validation[String, UUID] = for { str <- metadata.get("id").toSuccess("No id property") id <- str |> parseUUID |> justMessage } yield id Pipe-forward operator reverses order of function application (from F#) For more info on pipe-forward, see: http://stackoverflow.com/questions/1457140/haskell-composition-vs-fs-pipe-forward-operator

Slide 108

Slide 108 text

Validation • Monadic usage of Validation is incredibly useful • As useful as monadic option but provides why failures have occurred • Can it get better?

Slide 109

Slide 109 text

Validation is Applicative... ... if the error type is Semigroup

Slide 110

Slide 110 text

Validation is Applicative... ... if the error type is Semigroup Validation[Int, Any] Validation[Any, Int] Validation[List[String], String] Validation[String, Int] Which of these are applicative?

Slide 111

Slide 111 text

Validation is Applicative... ... if the error type is Semigroup Validation[Int, Any] Validation[Any, Int] Validation[List[String], String] Validation[String, Int] Which of these are applicative? ✔

Slide 112

Slide 112 text

Validation is Applicative... ... if the error type is Semigroup Validation[Int, Any] Validation[Any, Int] Validation[List[String], String] Validation[String, Int] Which of these are applicative? ✔ ✘

Slide 113

Slide 113 text

Validation is Applicative... ... if the error type is Semigroup Validation[Int, Any] Validation[Any, Int] Validation[List[String], String] Validation[String, Int] Which of these are applicative? ✔ ✘ ✔

Slide 114

Slide 114 text

Validation is Applicative... ... if the error type is Semigroup Validation[Int, Any] Validation[Any, Int] Validation[List[String], String] Validation[String, Int] Which of these are applicative? ✔ ✘ ✔ ✔

Slide 115

Slide 115 text

Validation[List[_], _] Validation[List[String], Int]

Slide 116

Slide 116 text

Success(42) Validation[List[_], _] Validation[List[String], Int]

Slide 117

Slide 117 text

Success(42) Failure(List(“Tigers”, “Lions”, “Bears”)) Validation[List[_], _] Validation[List[String], Int]

Slide 118

Slide 118 text

Success(42) Failure(List(“Tigers”, “Lions”, “Bears”)) Failure(List()) Validation[List[_], _] Validation[List[String], Int]

Slide 119

Slide 119 text

Success(42) Failure(List(“Tigers”, “Lions”, “Bears”)) Failure(List()) Validation[List[_], _] Validation[List[String], Int] Failure of no errors? Can we do better? Use the type system?

Slide 120

Slide 120 text

Success(42) Failure(NonEmptyList( “Tigers”, “Lions”, “Bears”)) ValidationNEL Validation[NonEmptyList[String], Int]

Slide 121

Slide 121 text

Success(42) Failure(NonEmptyList( “Tigers”, “Lions”, “Bears”)) ValidationNEL Validation[NonEmptyList[String], Int] ValidationNEL[String, Int] This is so common, there’s an alias for it:

Slide 122

Slide 122 text

Success(42) Failure(NonEmptyList( “Tigers”, “Lions”, “Bears”)) ValidationNEL Validation[NonEmptyList[String], Int] ValidationNEL[String, Int] This is so common, there’s an alias for it: And any Validation[E, S] can be converted to ValidationNEL[E, S]: v.toValidationNEL

Slide 123

Slide 123 text

Putting together the pieces • Given a map of string to string containing keys name, level, manager, representing the name of an employee, their numeric level, and true if they are an manager and false otherwise, create an Employee containing those values if: • Name is non-empty • Level is 1 to 15 • Otherwise, report all errors in the map

Slide 124

Slide 124 text

case class Employee( name: String, level: Int, manager: Boolean) type Metadata = Map[String, String] Putting together the pieces

Slide 125

Slide 125 text

def justMessage[S]( v: Validation[Throwable, S]): Validation[String, S] = v.<-: { _.getMessage } def extractString( metadata: Metadata, key: String): Validation[String, String] = metadata. get(key). toSuccess("Missing required property %s".format(key)) Putting together the pieces

Slide 126

Slide 126 text

def extractName( metadata: Metadata): Validation[String, String] = extractString(metadata, "name") flatMap { name => if (name.isEmpty) "Must have a non-empty name!".fail else name.success } Putting together the pieces

Slide 127

Slide 127 text

def extractLevel( metadata: Metadata): Validation[String, Int] = extractString(metadata, "level"). flatMap { _.parseInt |> justMessage }. flatMap { level => if (level < 1) "Level must be at least 1".fail else if (level > 15) "Really?".fail else level.success } Putting together the pieces

Slide 128

Slide 128 text

def extractManager( metadata: Metadata): Validation[String, Boolean] = extractString(metadata, "manager"). flatMap { _.parseBoolean |> justMessage } Putting together the pieces

Slide 129

Slide 129 text

def extractEmployee( metadata: Metadata): ValidationNEL[String, Employee] = { extractName(metadata).toValidationNel |@| extractLevel(metadata).toValidationNel |@| extractManager(metadata).toValidationNel } apply Employee.apply Putting together the pieces

Slide 130

Slide 130 text

extractEmployee(Map( "name" -> "Turing", "level" -> "15", "manager" -> "false")) extractEmployee(Map( "name" -> "Turing", "level" -> "0", "manager" -> "true")) extractEmployee(Map( "name" -> "Turing", "level" -> "17", "manager" -> "true")) Putting together the pieces

Slide 131

Slide 131 text

extractEmployee(Map( "name" -> "Turing", "level" -> "15", "manager" -> "false")) extractEmployee(Map( "name" -> "Turing", "level" -> "0", "manager" -> "true")) extractEmployee(Map( "name" -> "Turing", "level" -> "17", "manager" -> "true")) Putting together the pieces Success(Employee(Turing,15,false))

Slide 132

Slide 132 text

extractEmployee(Map( "name" -> "Turing", "level" -> "15", "manager" -> "false")) extractEmployee(Map( "name" -> "Turing", "level" -> "0", "manager" -> "true")) extractEmployee(Map( "name" -> "Turing", "level" -> "17", "manager" -> "true")) Putting together the pieces Success(Employee(Turing,15,false)) Failure(NonEmptyList(Level must be at least 1))

Slide 133

Slide 133 text

extractEmployee(Map( "name" -> "Turing", "level" -> "15", "manager" -> "false")) extractEmployee(Map( "name" -> "Turing", "level" -> "0", "manager" -> "true")) extractEmployee(Map( "name" -> "Turing", "level" -> "17", "manager" -> "true")) Putting together the pieces Success(Employee(Turing,15,false)) Failure(NonEmptyList(Level must be at least 1)) Failure(NonEmptyList(Really?))

Slide 134

Slide 134 text

extractEmployee(Map( "name" -> "Turing")) extractEmployee(Map( "name" -> "", "level" -> "17", "manager" -> "notBool")) Putting together the pieces

Slide 135

Slide 135 text

extractEmployee(Map( "name" -> "Turing")) extractEmployee(Map( "name" -> "", "level" -> "17", "manager" -> "notBool")) Putting together the pieces Failure(NonEmptyList( Missing required property level, Missing required property manager))

Slide 136

Slide 136 text

extractEmployee(Map( "name" -> "Turing")) extractEmployee(Map( "name" -> "", "level" -> "17", "manager" -> "notBool")) Putting together the pieces Failure(NonEmptyList( Missing required property level, Missing required property manager)) Failure(NonEmptyList( Must have a non-empty name!, Really?, For input string: "notBool"))

Slide 137

Slide 137 text

Putting together the pieces • Building upon previous example, given a list of metadata, produce a list of employees or a list of errors

Slide 138

Slide 138 text

Putting together the pieces def extractEmployees( metadata: List[Metadata] ): ValidationNEL[String, List[Employee]] = ???

Slide 139

Slide 139 text

Putting together the pieces def extractEmployees( metadata: List[Metadata] ): ValidationNEL[String, List[Employee]] = { val vEmps: List[ValidationNEL[String, Employee]] = metadata map extractEmployee ??? }

Slide 140

Slide 140 text

Putting together the pieces def extractEmployees( metadata: List[Metadata] ): ValidationNEL[String, List[Employee]] = { val vEmps: List[ValidationNEL[String, Employee]] = metadata map extractEmployee ??? } How can we swap order of ValidationNEL and List?

Slide 141

Slide 141 text

Putting together the pieces def extractEmployees( metadata: List[Metadata] ): ValidationNEL[String, List[Employee]] = { val vEmps: List[ValidationNEL[String, Employee]] = metadata map extractEmployee vEmps.sequence[ ({type λ[a] = ValidationNEL[String, a]})#λ, Employee] } Type annotation necessary due to limitations in Scala type inferencing

Slide 142

Slide 142 text

Type Lambdas sequence[({type λ[a] = ValidationNEL[String, a]})#λ, Employee]

Slide 143

Slide 143 text

Type Lambdas sequence[({type λ[a] = ValidationNEL[String, a]})#λ, Employee] Defines an anonymous structural type with one member, λ[a]

Slide 144

Slide 144 text

Type Lambdas Defines type λ[a] as an alias for ValidationNEL[String, a] sequence[({type λ[a] = ValidationNEL[String, a]})#λ, Employee]

Slide 145

Slide 145 text

Type Lambdas sequence[({type λ[a] = ValidationNEL[String, a]})#λ, Employee] References the λ member of the structural type

Slide 146

Slide 146 text

Type Lambdas Not valid Scala syntax, but IntelliJ will render type lambdas this way sequence[λ[a] = ValidationNEL[String, a], Employee]

Slide 147

Slide 147 text

Type Lambdas Can be thought of this way (sort of) sequence[ValidationNEL[String, _], Employee]

Slide 148

Slide 148 text

Using sequenceU def extractEmployees( metadata: List[Metadata] ): ValidationNEL[String, List[Employee]] = { val vEmps: List[ValidationNEL[String, Employee]] = metadata map extractEmployee vEmps.sequenceU } In this case, type params can be inferred via sequenceU

Slide 149

Slide 149 text

case class Contact( name: Name, phone: Phone) case class Name( salutation: String, first: String, last: String) case class Phone( digits: String) val seth = Contact( Name("Mr.", "Seth", "Avett"), Phone("555-5555")) Lens: Problem Statement

Slide 150

Slide 150 text

case class Contact( name: Name, phone: Phone) case class Name( salutation: String, first: String, last: String) case class Phone( digits: String) val seth = Contact( Name("Mr.", "Seth", "Avett"), Phone("555-5555")) Lens: Problem Statement We need a copy of seth with first name set to “Scott”

Slide 151

Slide 151 text

val seth = Contact( Name("Mr.", "Seth", "Avett"), Phone("555-5555")) val scott = seth.copy( name = seth.name.copy(first = "Scott")) Lens: Problem Statement

Slide 152

Slide 152 text

val seth = Contact( Name("Mr.", "Seth", "Avett"), Phone("555-5555")) val scott = seth.copy( name = seth.name.copy(first = "Scott")) Lens: Problem Statement Doesn’t scale - each layer in record structure results in another layer of copies and duplication

Slide 153

Slide 153 text

Lens • Given a record type and a field, a lens provides the ability to focus on the specified field, which allows accessing the field value and setting the field value (immutably)

Slide 154

Slide 154 text

Lens • Given a record type and a field, a lens provides the ability to focus on the specified field, which allows accessing the field value and setting the field value (immutably) • Translation: Lens = immutable getter/setter

Slide 155

Slide 155 text

case class Contact( name: Name, phone: Phone) val contactNameLens = Lens.lensu[Contact, Name]( (c, n) => c.copy(name = n), _.name) val contactPhoneLens = Lens.lensu[Contact, Phone]( (c, p) => c.copy(phone = p), _.phone) Lens Setter Getter Setter Getter Record Type Field Type

Slide 156

Slide 156 text

case class Contact( name: Name, phone: Phone) val contactNameLens = Lens.lensu[Contact, Name]( (c, n) => c.copy(name = n), _.name) val contactPhoneLens = Lens.lensu[Contact, Phone]( (c, p) => c.copy(phone = p), _.phone) Lens Setter Getter Setter Getter •Case class lenses usually use copy in setter

Slide 157

Slide 157 text

case class Contact( name: Name, phone: Phone) val contactNameLens = Lens.lensu[Contact, Name]( (c, n) => c.copy(name = n), _.name) val contactPhoneLens = Lens.lensu[Contact, Phone]( (c, p) => c.copy(phone = p), _.phone) Lens Setter Getter Setter Getter •Case class lenses usually use copy in setter •Case class lenses are purely mechanical to author (there’s even a compiler plugin that does it!)

Slide 158

Slide 158 text

case class Name( salutation: String, first: String, last: String) val nameSalutationLens = Lens.lensu[Name, String]( (n, s) => n.copy(salutation = s), _.salutation) val nameFirstLens = Lens.lensu[Name, String]( (n, f) => n.copy(first = f), _.first) val nameLastLens = Lens.lensu[Name, String]( (n, l) => n.copy(last = l), _.last) Lens

Slide 159

Slide 159 text

case class Phone( digits: String) val phoneDigitsLens = Lens.lensu[Phone, String]( (p, d) => p.copy(digits = d), _.digits) Lens

Slide 160

Slide 160 text

val contactFirstNameLens = contactNameLens >=> nameFirstLens Lens Lenses compose!! Composes 2 lenses in to a Contact to First Name lens via andThen

Slide 161

Slide 161 text

val seth = Contact( Name("Mr.", "Seth", "Avett"), Phone("555-5555")) val contactFirstNameLens = contactNameLens >=> nameFirstLens Lens Lenses compose!! Composes 2 lenses in to a Contact to First Name lens via andThen

Slide 162

Slide 162 text

val seth = Contact( Name("Mr.", "Seth", "Avett"), Phone("555-5555")) val contactFirstNameLens = contactNameLens >=> nameFirstLens Lens Lenses compose!! Composes 2 lenses in to a Contact to First Name lens via andThen contactFirstNameLens.get(seth) “Seth”

Slide 163

Slide 163 text

val seth = Contact( Name("Mr.", "Seth", "Avett"), Phone("555-5555")) val contactFirstNameLens = contactNameLens >=> nameFirstLens Lens Lenses compose!! Composes 2 lenses in to a Contact to First Name lens via andThen contactFirstNameLens.get(seth) “Seth” contactFirstNameLens.set(seth, "Scott") Contact(Name(Mr.,Scott,Avett),Phone(555-5555))

Slide 164

Slide 164 text

Future Topics • Zipper • Reader • Writer • State • Monad Transformers • Arrow • Kleisli • Category • Iteratees ...and much more!

Slide 165

Slide 165 text

Questions? Photo Credit: http://www.flickr.com/photos/yannconz/2795462449