Reader

Implementation

case class Reader[E,A](run: E => A)

object Reader:

  implicit def readerMonad[E]: Monad[[A] =>> Reader[E,A]] =
    new Monad[[A] =>> Reader[E,A]]:

      def pure[A](a: A): Reader[E,A] =
        Reader(_ => a)

      def flatMap[A,B](ma: Reader[E,A])(f: A => Reader[E,B]): Reader[E,B] =
        Reader { (e: E) =>
          val a: A = ma.run(e)
          f(a).run(e)
        }

Dependencies

Example

Services

trait LocationService:
  def getMyLocation: String

trait WeatherService:
  def getWeatherAt(location: String): String

Modules

trait LocationServiceIO:
  def locationService: LocationService

trait WeatherServiceIO:
  def weatherService: WeatherService

Effects

def getMyLocation[E <: LocationServiceIO]: Reader[E, String] =
  Reader(e => e.locationService.getMyLocation)

def getWeatherAt[E <: WeatherServiceIO](location: String): Reader[E, String] =
  Reader(e => e.weatherService.getWeatherAt(location))

Usage

import Monad._
import Reader._

type EnvIO = LocationServiceIO with WeatherServiceIO

val getMyWeather: Reader[EnvIO, String] =
  for
    myLocation <- getMyLocation[EnvIO] // requires Scala 2.13 (33478bd)
    weather    <- getWeatherAt[EnvIO](myLocation)
  yield s"It is ${weather} in ${myLocation}."
val env: EnvIO =
  new LocationServiceIO with WeatherServiceIO:

    val locationService: LocationService =
      new LocationService:
        def getMyLocation: String = "Boulder"

    val weatherService: WeatherService =
      new WeatherService:
        def getWeatherAt(location: String): String = "sunny"

val myWeather: String = getMyWeather.run(env)

println(myWeather) // It is sunny in Boulder.

Demo

This file is literate Scala, and can be run using Codedown:

$ curl \
    https://earldouglas.com/type-classes/applicative.md \
    https://earldouglas.com/type-classes/functor.md \
    https://earldouglas.com/type-classes/monad.md \
    https://earldouglas.com/type-classes/reader.md |
  codedown scala |
  scala-cli -q --scala 3.1.3 _.sc
It is sunny in Boulder.

References