Solutions to Scala with Cats: Chapter 8
April 7, 2023These are my solutions to the exercises of chapter 8 of Scala with Cats.
Table of Contents
Exercise 8.1: Abstracting over Type Constructors
To write a trait definition for UptimeClient
that abstracts over the return
types, we can add a type constructor F[_]
as a type parameter:
trait UptimeClient[F[_]] {
def getUptime(hostname: String): F[Int]
}
We can then extend it with two traits that bind F
to Future
and Id
respectively:
trait RealUptimeClient extends UptimeClient[Future]
trait TestUptimeClient extends UptimeClient[Id]
To make sure that the code compiles, we write out the method signatures for
getUptime
in each case:
trait RealUptimeClient extends UptimeClient[Future] {
def getUptime(hostname: String): Future[Int]
}
trait TestUptimeClient extends UptimeClient[Id] {
def getUptime(hostname: String): Id[Int]
}
We can now have a TestUptimeClient
as a full class based on Map[String, Int]
with no need for relying on Future
:
class TestUptimeClient(hosts: Map[String, Int]) extends UptimeClient[Id] {
def getUptime(hostname: String): Id[Int] =
hosts.getOrElse(hostname, 0)
}
Exercise 8.2: Abstracting over Monads
We can rewrite the method signatures of UptimeService
so that it abstracts
over the context as follows:
class UptimeService[F[_]](client: UptimeClient[F]) {
def getTotalUptime(hostnames: List[String]): F[Int] =
???
}
To get the previous implementation working, we need to not only prove the
compiler that F
has an Applicative
, but also add a few syntax imports so
that we can call traverse
and map
:
import cats.Applicative
import cats.instances.list._
import cats.syntax.functor._
import cats.syntax.traverse._
class UptimeService[F[_]: Applicative](client: UptimeClient[F]) {
def getTotalUptime(hostnames: List[String]): F[Int] =
hostnames.traverse(client.getUptime).map(_.sum)
}