5

I read Dependency Injection Without the Gymnastics PDF which indicates there's no need for any fancy DI framework, but it's beyond my grasp (at least without concrete examples). I'll try watching Dependency Injection Without the Gymnastics and Dead Simple Dependency Injection when I have a chance.

Using Guice in Java, if A depends on both B and C and both B and C depend on D, one would have something like:

public class A {
 @Inject
 public A(B b, C c) {
 this.b = b;
 this.c = c;
 }
}
public class B {
 @Inject
 public B(D d) {
 this.d = d;
 }
}
public class C {
 @Inject
 public C(D d) {
 this.d = d;
 }
}
public class D { /* ... */ }

and a module that says which implementation of D to use, then one would just ask for an instance of A from the injector:

A a = injector.createInstance(A.class);

Given what's presented in the URLs above, how would the Scala-equivalent of the above code look?

FWIW, I'm also investigating https://github.com/dickwall/subcut/blob/master/GettingStarted.md and am simply trying to understand the anti-DI solution.

asked Sep 9, 2012 at 18:42
0

4 Answers 4

8

Implicit parameters are completely sufficient for the use case you're describing.

case class A(implicit b: B, c: C)
case class B(implicit d: D)
case class C(implicit d: D)
class D { /* ... */ }
implicit val theD = new D
implicit val theB = B()
implicit val theC = C()

Now you can ask for an A just by:

val a = A()
answered Sep 10, 2012 at 4:22
4
  • "In Scala 2.10, you can write implicit class instead of separately writing the class and the implicit instance." Eh? No you can't. Or am I the confused one? I think implicit class is about implicit conversions, not implicit parameters. Commented Sep 10, 2012 at 23:28
  • Seth, you're totally right, an implicit class can't take implicit parameters at all. Commented Sep 11, 2012 at 2:02
  • I think this and/or using the Reader Monad is the closest one can get without a framework. The downside is that there's no auto-wiring and people will have to instantiate objects in the right order. Commented Sep 11, 2012 at 13:43
  • 3
    That's simply not true. The implicit resolution performs the same steps as "auto-wiring". Commented Sep 11, 2012 at 13:50
4

You may solve it with self-types.

A depends on both B and C and both B and C depend on D

so one could write this like that:

class A {
 self: B with C => 
}
trait B { 
 self: D => 
}
trait C {
 self: D => 
}
trait D {}

and then on a call side:

val x = new A with BImpl with CImpl with DImpl

but code below won't compile, because dependencies on B,C,D classes not resolved:

val x = new A
answered Sep 9, 2012 at 18:58
9
  • If it doesn't compile, it's not a complete solution. I'd say a complete solution allows the user to specify only the implementation of D to use and be able to create an instance of A. Commented Sep 10, 2012 at 3:27
  • @NoelYap, I think you misunderstood; the second last code line compiles giving the desired instance of A. Commented Sep 10, 2012 at 9:50
  • @ScottMorrison, yes, I did misunderstand. I think the solution is still not that good, though, since it requires the user of A to know the dependence of B and C upon D (IIUC, 'with DImpl' has to go after the other 'with' clauses). It's also not very scalable because of this (in essence, the user of A would have to perform the topological sort of the entire dependency graph). Commented Sep 10, 2012 at 13:49
  • @NoelYap new A with DImpl with BImpl with CImpl is perfectly legal due to linerization (generally, that wouldn't be a problem to place aspects in different order) or you mean something different? Commented Sep 10, 2012 at 13:53
  • I think he means that he would have to know that D depends on B and C. That's trivial in this example but D may actually transitively depend on a lot of things. Also doing this you end up with one big object with everything baked in (pun intended) versus separate objects. This may or may not matter depending on your situation but it's something to consider. Commented Oct 14, 2012 at 23:58
1

It's tricky to provide that type of dependency injection. Most of the above examples require you to create the implicits near where the classes are instantiated.

Closest I could come up with is:

class A(implicit b:B, c:C)
class B(implicit d:D)
class C(implicit d:D)
trait D { //the interface 
 def x:Unit
}
object Implicits {
 implicit def aFactory:A = new A
 implicit lazy val bInstance:B = new B
 implicit def cFactory:C = new C
 implicit def dFactory:D = new D {
 def x:Unit = {/* some code */}
 }
}

And then in your code you use it like this:

import Implicits._
object MyApplication {
 def main(args: Array[String]):Unit = {
 val a = new A
 }
}

If you need to be able to specify different versions when you (for example) are testing, you could do something like this:

import Implicits._
object MyApplication {
 // Define the actual implicits
 Implicits.module = new Module {
 import Implicits._
 def a = new A
 lazy val b = new B
 def c = new C
 def d = new D {
 def x = println("x")
 }
 }
 def main(args: Array[String]):Unit = {
 val a = new A // or val a = implicitly[A] 
 }
}
// The contract (all elements that you need)
trait Module {
 def a: A
 def b: B
 def c: C
 def d: D
}
// Making the contract available as implicits
object Implicits {
 var module: Module = _
 implicit def aFactory:A = module.a
 implicit def bFactory:B = module.b
 implicit def cFactory:C = module.c
 implicit def dFactory:D = module.d
}

This would allow you to simply import Implicits._ in any file and would provide a similar workflow as the one in the original question.

In most cases however I would not use this tactic. I would simply make the implicit available in classes that create instances:

object MyApplication {
 implicit def a: A = new A
 implicit lazy val b: B = new B
 implicit def c: C = new C
 implicit def d: D = new D {
 def x: Unit = println("x")
 }
 def main(args: Array[String]): Unit = {
 val a = implicitly[A]
 val e = new E
 }
}
class E(implicit d:D) {
 new C
}

Here E is defined in another file and creates an instance of C. We require D to be passed to E and with that document that E depends on D (via C).

answered Dec 13, 2012 at 22:30
0

I think @om-nom-nom's answer is quite close to what you want. Here is what I've got:

class A {
 self: B with C => 
 def sum = tripleD + doubleD
}
trait B { 
 self: D => 
 def tripleD = x * 3
}
trait C {
 self: D => 
 def doubleD = x * 2
}
trait D extends B with C {
 val x: Int
}
trait E extends D {
 val x = 3
}
trait F extends D {
 val x = 4
}
val a = new A with E
val b = new A with F
println("a.sum = " + a.sum)
println("b.sum = " + b.sum)
answered Sep 11, 2012 at 2:05
1
  • That solution requires D to know about B and C even though the dependency is the other way around. Commented Sep 11, 2012 at 4:22

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.