Created
January 21, 2013 22:46
-
-
Save przemek-pokrywka/4590235 to your computer and use it in GitHub Desktop.
Reader Monad dependency injection when each of injected functions depends on something other. The goal is to assess, how good does Reader Monad fit into this context.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Reader[Conf, Res](obtainResult: Conf => Res) { | |
def map[NewRes](transform: Res => NewRes) = | |
Reader { | |
(config: Conf) => | |
transform(this obtainResult config) | |
} | |
def flatMap[NewRes](createReader: Res => Reader[Conf, NewRes]) = | |
Reader { | |
(config: Conf) => | |
val newReader = createReader(this obtainResult config) | |
newReader obtainResult config | |
} | |
} | |
/** Demonstrates usage of Reader Monad for dependency injection | |
* when each of injected functions depends on something other | |
*/ | |
object Demo extends App { | |
// Some "domain model" | |
trait GUI { def display(s: String): Unit } | |
trait DB { def crud(a: Any): Unit } | |
case class ConsoleGUI extends GUI { def display(s: String) = println(s) } | |
case class SimpleDB extends DB { def crud(a: Any) = println("cruded " + a) } | |
// "business logic" | |
// Reader Monad requires it in form of curried functions | |
// Point of this exercise is that the functions have different dependencies | |
// - first depends on GUI, second on DB | |
val blink = (times: Int) => (gui: GUI) => | |
(1 to times).map(_.toString).foreach(gui.display _) | |
val saveAll = (times: Int) => (db: DB) => | |
(1 to times).foreach(db.crud _) | |
// Plumbing required by Reader Monad in this case. | |
// A type that groups together all dependencies to be injected | |
type Ctx = (GUI, DB) | |
// Conversions needed to use "business logic" functions unchanged | |
implicit def guiFun2CtxFun(guiFn: GUI => Unit): Ctx => Unit = | |
ctx => guiFn(ctx._1) | |
implicit def dbFun2CtxFun(dbFn: DB => Unit): Ctx => Unit = | |
ctx => dbFn(ctx._2) | |
// Program preparation | |
val program = for { | |
_ <- Reader[Ctx, Unit](blink(2)) | |
_ <- Reader[Ctx, Unit](saveAll(5)) | |
} yield () | |
// Injection and execution at once | |
program obtainResult ((ConsoleGUI(), SimpleDB())) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment