monads and friends demystified

25
Monads and friends demystified

Upload: alessandro-lacava

Post on 14-Apr-2017

580 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Monads and friends demystified

Monads and friends demystified

Page 2: Monads and friends demystified

About me

Blog: http://www.alessandrolacava.com

GitHub: https://github.com/lambdista

Twitter: https://twitter.com/lambdista

LinkedIn: https://www.linkedin.com/in/alessandrolacava

Page 3: Monads and friends demystified

Type Classes 101// 1. Capture a concepttrait Show[A] { def show(a: A): String}

object Show { // 2. Define a method that accept, implicitly, an instance of the concept def show[A](a: A)(implicit ev: Show[A]): String = ev.show(a)

}

// 3. Implement some instances of the conceptimplicit val intShow = new Show[Int] { override def show(a: Int): String = a.toString}

Page 4: Monads and friends demystified

Type Classes 102

object Show { // Alternative syntax def show[A: Show](a: A): String = implicitly[Show[A]].show(a)}

Page 5: Monads and friends demystified

Type Classes 103

object Show { // Commonly used pattern def apply[A: Show] = implicitly[Show[A]]

// All methods using Show can now use this syntax def show[A: Show](a: A): String = Show[A].show(a)}

Page 6: Monads and friends demystified

Higher-kinded typesKinds in Type Theory

Symbol Kind Examples* Simple type. AKA nullary

type constructor or proper type.

Int, String, Double, ...

* -> * Unary type constructor. List, Option, Set, ...

* -> * -> * Binary type constructor. Either, Function1, Map, ...

(* -> *) -> * Higher-order type operator, higher-kinded type for friends.

Foo[F[_]], Bar[G[_]], Functor[F[_]], Monad[M[_]], ...

Page 7: Monads and friends demystified

Example: MappableCapturing the concept represented by the map method (See: Option, …):

trait Mappable[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B]}

object Mappable { def apply[F[_]: Mappable]: Mappable[F] = implicitly[Mappable[F]]

def map[F[_]: Mappable, A, B](ma: F[A])(f: A => B): F[B] = Mappable[F].map(ma)(f)

implicit val optionMapper = new Mappaple[Option] { override def map[A, B](oa: Option[A])(f: A => B): Option[B] = oa.map(f) }

implicit val listMapper = new Mappaple[List] { override def map[A, B](la: List[A])(f: A => B): List[B] = la.map(f) }}

Page 8: Monads and friends demystified

Functor: Change the Mappable name...trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B]}

object Functor { def apply[F[_]: Functor]: Functor[F] = implicitly[Functor[F]]

def map[F[_]: Functor, A, B](ma: F[A])(f: A => B): F[B] = Functor[F].map(ma)(f) implicit val optionFunctor = new Functor[Option] { override def map[A, B](oa: Option[A])(f: A => B): Option[B] = oa.map(f) } implicit val listFunctor = new Functor[List] { override def map[A, B](la: List[A])(f: A => B): List[B] = la.map(f) }}

Page 9: Monads and friends demystified

...and add a couple of laws

In order for something to be a functor, it should satisfy a couple of laws:

Given a functor fa: F[A] and the identity function defined as follows:

def identity[A](a: A): A = a

the following laws must hold (where the order of the args is swapped, the Haskell way):

Identity Law: map(identity)(fa) = fa

Composition Law: map(f compose g)(fa) = map(f)(map(g)(fa))

Page 10: Monads and friends demystified

Identity Law

map(identity)(fa) = fa => map(identity) = identity

E.g.: Option[A]

import Functor._

val some: Option[Int] = Some(42)val none: Option[Int] = None

assert(map(some)(identity) == some)

assert(map(none)(identity) == none)

Page 11: Monads and friends demystified

Composition Law

map(f compose g)(fa) = map(f)(map(g)(fa))

E.g.: Option[A]

val f: Int => Int = x => x + 42val g: Int => Int = x => x * x

assert(map(some)(f compose g) == map(map(some)(g))(f))

assert(map(none)(f compose g) == map(map(none)(g))(f))

Note: The compiler cannot enforce these laws. You need to ensure them yourself.

Page 12: Monads and friends demystified

Lessons Learned

The Mappable concept was easier to get because the name didn’t scare you.

Rule I: Don’t let the buzzwords scare you

Everything gets easier if you look closely at the types.

Rule II: Always follow the types.

Page 13: Monads and friends demystified

Case StudyA Functor lets us apply a function to a value which is inside a context. Context examples: Option, List, Future.

Now, what if the function you want to apply is within a context as well?

E.g.:

def interpret(str: String): Option[Int => Int] = str.toLowerCase match { case "incr" => Some(_ + 1) case "decr" => Some(_ - 1) case "square" => Some(x => x * x) case "halve" => Some(x => x / 2) case _ => None}

val v: Option[Int] = Some(42)

Page 14: Monads and friends demystified

Enter The Applicative FunctorCapturing the concept of a function within a context, that is F[A => B]:

trait 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] // from applicative you get a functor for free override def map[A, B](fa: F[A])(fab: A => B): F[B] = }

object Applicative { def apply[F[_]: Applicative]: Applicative[F] = implicitly[Applicative[F]]

def pure[F[_]: Applicative, A](a: A): F[A] = Applicative[F].pure(a)

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

// ... Applicative instances}

ap(fa)(pure(fab))

Page 15: Monads and friends demystified

Applicative instancesobject Applicative {// ...implicit val optionApplicative = new Applicative[Option] { override def pure[A](a: A): Option[A] = Option(a)

override def ap[A, B](fa: Option[A])(fab: Option[A => B]): Option[B] = for { a <- fa f <- fab } yield f(a) }

implicit val listApplicative = new Applicative[List] { override def pure[A](a: A): List[A] = List(a)

override def ap[A, B](fa: List[A])(fab: List[A => B]): List[B] = for { a <- fa f <- fab } yield f(a) }}

The Applicative Functor needs some laws satisfied as well that we won’t cover here.

Page 16: Monads and friends demystified

Case Study Solvedimport Applicative._

def interpret(str: String): Option[Int => Int] = str.toLowerCase match { case "incr" => Some(_ + 1) case "decr" => Some(_ - 1) case "square" => Some(x => x * x) case "halve" => Some(x => x / 2) case _ => None}

val func: Option[Int => Int] = interpret("incr")

val v: Option[Int] = Some(42)

val result: Option[Int] = ap(v)(func)

Page 17: Monads and friends demystified

And now the dreadful MonadIf C is a Category a monad on C consists of an endofunctor T: C -> C together with two natural transformations: η: 1C -> T (where 1C denotes the identity functor on C)

μ: T2 -> T (where T2 is the functor T T from C to C).

These are required to fulfill the following conditions (sometimes called coherence conditions):

μ Tμ = μ μ T (as natural transformations T3 -> T);

μ T η = μ η T = 1T (as natural transformations T -> T; here 1T denotes the identity transformation from T to T).

Just Kidding!Well, not really. That’s the Wikipedia definition

Page 18: Monads and friends demystified

Monad for developerstrait Monad[M[_]] extends Applicative[M] { def unit[A](a: A): M[A]

def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B]

override def pure[A](a: A): M[A] = unit(a)

override def ap[A, B](fa: M[A])(f: M[A => B]): M[B] = flatMap(fa)(a => map(f)(ff => ff(a)))}

The primitives required by the Monad type class are just unit and flatMap or, equivalently, unit and join (along with map inherited from Functor):

def join(mma: M[M[A]]): M[A]

You can derive flatMap from unit, join and map.

Page 19: Monads and friends demystified

Monad continued...object Monad { def apply[M[_] : Monad]: Monad[M] = implicitly[Monad[M]]

def unit[M[_]: Monad, A](a: A): M[A] = Monad[M].unit(a)

def flatMap[M[_]: Monad, A, B](ma: M[A])(f: A => M[B]): M[B] = Monad[M].flatMap(ma)(f)

implicit val optionMonad = new Monad[Option] { override def unit[A](a: A): Option[A] = Option(a)

override def flatMap[A, B](ma: Option[A])(f: A => Option[B]): Option[B] = ma.flatMap(f) }

implicit val listMonad = new Monad[List] { override def unit[A](a: A): List[A] = List(a)

override def flatMap[A, B](ma: List[A])(f: A => List[B]): List[B] = ma.flatMap(f) }}

Page 20: Monads and friends demystified

Monad Laws

Do we need to write our own type classes and implementations for concepts such as Functor, Applicative and Monad?

In Scala pseudocode:

Left Identity: unit(a).flatMap(f) == f(a)

Right Identity: m.flatMap(unit) == m

Associativity: (m.flatMap(f)).flatMap(g) == m.flatMap(a => f(a).flatMap(g))

Not really!

Page 21: Monads and friends demystified

CatsGitHub: https://github.com/typelevel/cats

libraryDependencies += "org.typelevel" %% "cats" % catsVersion

Resources for Learners: http://typelevel.org/cats/ http://eed3si9n.com/herding-cats/ Cats is broken up into a number of sub-projects:

● core - contains type class definitions (e.g. Functor, Applicative, Monad), essential datatypes, and type class instances for those datatypes and standard library types

● laws - laws for the type classes, used to validate type class instances● tests - tests that check type class instances with laws from laws● docs - The source for this website

Page 22: Monads and friends demystified

Cats (0.4.0): Exampleimport cats._import cats.std.all._

val inc: Int => Int = _ + 1

val some: Option[Int] = Some(42)val none: Option[Int] = None

val list: List[Int] = List(1, 2, 3)val emptyList: List[Int] = List()

val optResult1 = Functor[Option].map(some)(inc) // Some(43)val optResult2 = Functor[Option].map(none)(inc) // None

val listResult1 = Functor[List].map(list)(inc) // List(2, 3, 4)val listResult2 = Functor[List].map(emptyList)(inc) // List()

Nice. But it would be more useful if we could abstract over Functor, wouldn’t it?

Page 23: Monads and friends demystified

Cats (0.4.0): Abstracting over Functor

val inc: Int => Int = _ + 1

val some: Option[Int] = Some(42)val none: Option[Int] = None

val list: List[Int] = List(1, 2, 3)val emptyList: List[Int] = List()

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

val optResult3 = map(some)(inc) // Some(43)

val listResult3 = map(list)(inc) // List(2, 3, 4)

Great. Now what if we want to compose two functors? No problem man!

Page 24: Monads and friends demystified

Cats (0.4.0): Functors compose!val inc: Int => Int = _ + 1

val listOfOpts = List(Some(1), None, Some(3))

// We want to map inc to listOfOpts

val listOptFunctor = Functor[List] compose Functor[Option]

val listOptResult = listOptFunctor.map(listOfOpts)(inc) // List(Some(2), None, Some(4))

Applicatives compose too. Monads do not, mechanically, compose. Some of them do and their composition is generally achieved through Monad transformers.

Page 25: Monads and friends demystified

Shameless Plug

I dealt with these and other subjects in this book: Professional Scala - Wrox

URL: http://eu.wiley.com/WileyCDA/WileyTitle/productCd-1119267226.html