Solutions to "Scala with Cats": Chapter 8

April 7, 2023

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