Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Reader Monad for Dependency Injection

Tags:

I recently saw the talks Dead-Simple Dependency Injection and Dependency Injection Without the Gymnastics about DI with Monads and was impressed. I tried to apply it on a simple problem, but failed as soon as it got non-trivial. I really would like to see a running version of dependency injection where

  • a class that depends on more than one value that has to be injected
  • a class that depends on a class that depends on something to be injected

as in the following example

trait FlyBehaviour { def fly() } trait QuackBehaviour { def quack() } trait Animal { def makeSound() }  // needs two behaviours injected class Duck(val flyBehaviour: FlyBehaviour, val quackBehaviour: QuackBehaviour) extends Animal  {    def quack() = quackBehaviour.quack()    def fly() = flyBehaviour.fly()    def makeSound() = quack() }  // needs an Animal injected (e.g. a Duck) class Zoo(val animal: Animal)  // Spring for example would be able to provide a Zoo instance // assuming a Zoo in configured to get a Duck injected and // a Duck is configured to get impl. of FlyBehaviour and QuackBehaviour injected val zoo: Zoo = InjectionFramework.get("Zoo") zoo.animal.makeSound() 

It would be really helpful to see a sample implementation using the reader Monad since I just feel that I am missing a push in the right direction.

Thanks!

like image 564
Manuel Schmidt Avatar asked Jun 30 '12 17:06

Manuel Schmidt


1 Answers

The "reader monad" is just Function1, so all you need to do is accept an argument containing all the things you need. For example:

trait Config {    def fly: FlyBehaviour    def quack: QuackBehaviour }  type Env[A] = Config => A 

Now if you want to construct a Duck based on this environment:

val a: Env[Animal] = c => new Duck(c.fly, c.quack) 

And then constructing a Zoo based on that is easy:

val z: Env[Zoo] = a andThen (new Zoo(_)) 

Using Scalaz (or with a bit of work on your own) you can make use of some syntax niceties to "ask" for the config c:

val z: Env[Zoo] = for {   c <- ask } yield new Zoo(Duck(c.fly, c.quack)) 
like image 132
Apocalisp Avatar answered Oct 14 '22 00:10

Apocalisp