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)
}