Jakub Kozłowski - Scala Developer, Scalac
Berlin Scala | June 21, 2018 | Berlin, GermanyTypeclasses
Functor
Applicative
Monad
Traverse
typelevel project
"Lightweight, modular, and extensible library for functional programming"
Inspired by Scalaz
Provides type classes, instances and data structures
uber helpful chatroom at
Example
trait Empty[T]{
def empty(): T
}
Provides an "empty" instance of T
def twoEmpties[T](implicit e: Empty[T]): (T, T) = (e.empty(), e.empty())
implicit val emptyInt: Empty[Int] = new Empty[Int] {
def empty(): Int = 0
}
scala> val (a, b) = twoEmpties[Int]
a: Int = 0
b: Int = 0
scala> val (s1, s2) = twoEmpties[String]
<console>:13: error: could not find
implicit value for parameter e: Empty[String]
val (s1, s2) = twoEmpties[String]
^
implicit val emptyString: Empty[String] = () => ""
scala> val (s1, s2) = twoEmpties[String]
s1: String = ""
s2: String = ""
Conventions
trait Empty[T]{
def empty(): T
}
object Empty {
def apply[T](implicit ev: Empty[T]): Empty[T] = ev
}
//syntactic sugar for def twoEmpties[T](implicit ev$1: Empty[T])
def twoEmpties[T : Empty]: (T, T) =
(Empty[T].empty(), Empty[T].empty())
//e.g. List(1,2,3).sorted
def sorted[B >: A](implicit ord: Ordering[B])
//e.g. List(1,2,3).sum
def sum[B >: A](implicit num: Numeric[B]): B
trait Functor[F[_]]{
def map[T, U](ts: F[T])(f: T => U): F[U]
}
def mapPlusOne[F[_] : Functor](ints: F[Int]): F[Int] = {
Functor[F].map(ints)(_ + 1)
//or
import cats.syntax.functor._
ints.map(_ + 1)
}
mapPlusOne(List(1, 2, 3)) // List(2, 3, 4)
mapPlusOne(Option(3)) // Some(4): Option[Int]
val f = mapPlusOne((k: String) => k.length) //Functor[String => ?]
f("a") //2
trait Applicative[F[_]] extends Functor[F]{ //simplified
def pure[A](x: A): F[A]
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
}
(
User("Mike").pure[Future],
findUserF(1),
findUsersF()
).mapN((u1, u2, users) => u1 :: u2 :: users)
trait Monad[F[_]] extends Applicative[F]{
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
for {
user <- findUserF(1)
friends <- findFriendsF(user)
} yield friends
trait Traverse[F[_]] extends Functor[F]{
def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
}
val strings : List[Future[String]]
strings.sequence: Future[List[String]]
val findParent : User => Future[Parent]
val users : List[User]
users.traverse(findParentF): Future[List[Parent]]
xs.traverse(f) <-> xs.map(f).sequence
xs.sequence <-> xs.traverse(x => x) <-> xs.traverse(identity)
Task/IO are referentially transparent, Future is not.
Code time? Code time.
Typeclasses in Scala (Scalac blog)
Scala with Cats (NEW book from Underscore)
Slides: kubukoz.github.io/fantastic-monads-slides
Code: github.com/kubukoz/fantastic-monads-code
Questions?
Contact me: