Dependency injection in Scala

June 08, 2014

Given a function run and a class Reader that wraps it:

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

  def map[B](g: A => B): Reader[E,B] = ???

  def flatMap[B](g: A => Reader[E,B]): Reader[E,B] = ???

}

And given a couple of functions with dependencies:

val timesTwo: Int => Int = x => x * 2

val plusTwo: Int => Int = x => x + 2

Implement map and flatMap so that we can use Reader in a for-comprehension:

def get(k: String): Reader[Map[String,Int],Int] =
  Reader(m => m(k))

val program: Reader[Map[String,Int],Int] =
  for {
    x <- get("x")
    a  = timesTwo(x)
    y <- get("y")
    b  = plusTwo(y)
  } yield a * b

println(program.run(Map("x" -> 3, "y" -> 5))) // 42

Bonus

Implement inject to automatically lift functions with dependencies into a configuration-specific Reader:

trait Config {
  def x: Int
  def y: Int
}

val program2: Reader[Config,Int] =
  for {
    a <- timesTwo.inject(_.x)
    b <- plusTwo.inject(_.y)
  } yield a * b

println(program2.run(new Config { val x = 3; val y = 5 })) // 42