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!
1 Answer 1
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))
-
Thanks for your example. I am curious, are you (or are you aware of any bigger projects) using this in place of spring or goolge guice for DI? Is it preferable according to your experience?Manuel Schmidt– Manuel Schmidt2012年07月02日 17:59:35 +00:00Commented Jul 2, 2012 at 17:59
-
2Yes, we are using this extensively and it is much preferable. If I ever see Spring or Guice again it will be too soon.Apocalisp– Apocalisp2012年07月02日 19:02:16 +00:00Commented Jul 2, 2012 at 19:02
-
@Apocalisp, could you help me apply this solution to stackoverflow.com/questions/12341867/…?Noel Yap– Noel Yap2012年09月11日 05:18:02 +00:00Commented Sep 11, 2012 at 5:18
-
I'm a bit confused by this last example.
ask
returns aReader[Config, Config]
, but thennew Duck...
returns aDuck
, doesn't it? How does it magically turn into a, I suppose,Reader[Config, Duck]
? Or maybe it was meant to reada = new Duck...
?Nicolas Rinaudo– Nicolas Rinaudo2015年03月27日 17:54:05 +00:00Commented Mar 27, 2015 at 17:54
Explore related questions
See similar questions with these tags.