{ 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 concept implicit val intShow = new Show[Int] { override def show(a: Int): String = a.toString }
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))
f: Int => Int = x => x + 42 val 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.
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.
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)
= 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.
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)
a monad on C consists of an endofunctor T: C -> C together with two natural transformations: η: 1 C -> T (where 1 C 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 = 1 T (as natural transformations T -> T; here 1 T denotes the identity transformation from T to T). Just Kidding! Well, not really. That’s the Wikipedia definition
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.
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!
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
=> 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?
= _ + 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!
_ + 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.