implicit val intWriter:HtmlWriter[Int] = new HtmlWriter[Int] { override def write(value: Int): String = value.toString } implicit val stringWriter:HtmlWriter[String] = new HtmlWriter[String] { override def write(value: String): String = value.replaceAll("<", "<").replaceAll(">", ">") } The type class itself is a trait with a single type parameter Type class instances
implicit val intWriter:HtmlWriter[Int] = new HtmlWriter[Int] { override def write(value: Int): String = value.toString } implicit val stringWriter:HtmlWriter[String] = new HtmlWriter[String] { override def write(value: String): String = value.replaceAll("<", "<").replaceAll(">", ">") } implicit def boxWriter[A]:HtmlWriter[Box[A]] = new HtmlWriter[Box[A]] { override def write(value: Box[A]): String = ??? } The type class itself is a trait with a single type parameter Type class instances Extra type class instances
implicit val intWriter:HtmlWriter[Int] = new HtmlWriter[Int] { override def write(value: Int): String = value.toString } implicit val stringWriter:HtmlWriter[String] = new HtmlWriter[String] { override def write(value: String): String = value.replaceAll("<", "<").replaceAll(">", ">") } implicit def boxWriter[A]:HtmlWriter[Box[A]] = new HtmlWriter[Box[A]] { override def write(value: Box[A]): String = ??? } The type class itself is a trait with a single type parameter Type class instances Extra type class instances What are typeclasses? | Scala Haskell
identity(Option(1)) * Composition: F[f o g] = F[f] o F[g] */ def map[A, B](fa: F[A])(f: A => B): F[B] } implicit val optionFunctor: Functor[Option] = new Functor[Option] { def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match { case None => None case Some(a) => Some(f(a)) } } A functor is a way to apply a function over or around some structure that we don’t want to alter it
identity(Option(1)) * Composition: F[f o g] = F[f] o F[g] */ def map[A, B](fa: F[A])(f: A => B): F[B] } implicit val optionFunctor: Functor[Option] = new Functor[Option] { def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match { case None => None case Some(a) => Some(f(a)) } } A functor is a way to apply a function over or around some structure that we don’t want to alter it def mapOption[A, B](as: Option[A]) (f: A => B) (implicit functor: Functor[Option]): Option[B] = functor.map(as)(f)
implicit val listFunctor: Functor[List] = new Functor[List] { def map[A, B](fa: List[A])(f: A => B): List[B] = fa match { case Nil => Nil case a :: as => f(a) :: map(as)(f) } } Example
implicit val listFunctor: Functor[List] = new Functor[List] { def map[A, B](fa: List[A])(f: A => B): List[B] = fa match { case Nil => Nil case a :: as => f(a) :: map(as)(f) } } Example
function over or around some structure that we don’t want to alter it trait Applicative[F[_]] extends Functor[F] { def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] def pure[A](a: A): F[A] def map[A, B](fa: F[A])(f: A => B): F[B] = ap(pure(f))(fa) }
function over or around some structure that we don’t want to alter it trait Applicative[F[_]] extends Functor[F] { def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] def pure[A](a: A): F[A] def map[A, B](fa: F[A])(f: A => B): F[B] = ap(pure(f))(fa) } implicit val optionApplicative = new Applicative[Option] { def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] = (ff, fa) match { case (Some(f), Some(a)) => Some(f(a)) case _ => None } def pure[A](a: A): Option[A] = Option(a) }
sequentially and the results of previous computations may influence what computations to run next trait Monad[F[_]] extends Applicative[F] { def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = flatMap(ff)(x => map(fa)(x)) }
sequentially and the results of previous computations may influence what computations to run next trait Monad[F[_]] extends Applicative[F] { def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = flatMap(ff)(x => map(fa)(x)) } val optionMonad = new Monad[Option] { def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa match { case None => None case Some(a) => f(a) } def pure[A](a: A): Option[A] = Some(a) }
= Monads do not compose val greeting: Option[String] = Some("Hello") val firstName: Option[String] = Some("John") val lastName: Option[String] = Some("Doe") for { g <- greetingOption f <- firstNameOption l <- lastNameOption } yield s"$g $f $l" Same monads
= Monads do not compose val greetingFuture: Future[String] = Future.successful("Hello") val firstNameOption: Option[String] = Some("John") val lastNameOption: Option[String] = Some("Doe") for { g <- greetingFuture f <- firstNameOption l <- lastNameOption } yield s"$g $f $l" val greeting: Option[String] = Some("Hello") val firstName: Option[String] = Some("John") val lastName: Option[String] = Some("Doe") for { g <- greetingOption f <- firstNameOption l <- lastNameOption } yield s"$g $f $l" Same monads Different monads
= Monads do not compose val greetingFuture: Future[String] = Future.successful("Hello") val firstNameOption: Option[String] = Some("John") val lastNameOption: Option[String] = Some("Doe") for { g <- greetingFuture f <- firstNameOption l <- lastNameOption } yield s"$g $f $l" val greeting: Option[String] = Some("Hello") val firstName: Option[String] = Some("John") val lastName: Option[String] = Some("Doe") for { g <- greetingOption f <- firstNameOption l <- lastNameOption } yield s"$g $f $l" Same monads Different monads
= Monads do not compose val greetingFuture: Future[String] = Future.successful("Hello") val firstNameOption: Option[String] = Some("John") val lastNameOption: Option[String] = Some("Doe") for { g <- greetingFuture f <- firstNameOption l <- lastNameOption } yield s"$g $f $l" val greeting: Option[String] = Some("Hello") val firstName: Option[String] = Some("John") val lastName: Option[String] = Some("Doe") for { g <- greetingOption f <- firstNameOption l <- lastNameOption } yield s"$g $f $l" Same monads Monads (in general) do not compose Different monads
us a way to stack two monads on top of each other and have them act a single monad. Future[Either[Throwable, String]] => EitherT[Future, Throwable, String] EitherT[F[_], A, B] ^ |__ ANY MONAD!
of nesting Gives us a way to stack two monads on top of each other and have them act a single monad. Future[Either[Throwable, String]] => EitherT[Future, Throwable, String] EitherT[F[_], A, B] ^ |__ ANY MONAD!
of nesting We have a stack Gives us a way to stack two monads on top of each other and have them act a single monad. Future[Either[Throwable, String]] => EitherT[Future, Throwable, String] EitherT[F[_], A, B] ^ |__ ANY MONAD!
of nesting We have a stack Gives us a way to stack two monads on top of each other and have them act a single monad. Future[Either[Throwable, String]] => EitherT[Future, Throwable, String] There exists a transformer equivalent for every monad instance Monad Transformers Monad Transformers | | Scala Scala Haskell Haskell EitherT[F[_], A, B] ^ |__ ANY MONAD!
in Scala Learn You a Haskell for Great Good! Haskell Programming from first principles Cats Scalaz Category Theory for Programmers Demystifying Functional Programming (code examples)