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
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!
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With