具有构造函数依赖项注入的Scala类型类

时间:2018-10-17 21:51:53

标签: scala dependency-injection typeclass

我试图弄清楚如何使传统的基于构造函数的依赖注入与类型类模式一起工作。

例如,给定

trait MyTypeClass[A] {
  def doSomething(a: A): Unit
}

class TypeClasses(prefix: String) {

  implicit val stringTC = new MyTypeClass[String] {
    def doSomething(a: String) = println(s"$prefix a")
  }

  implicit val intTc = new MyTypeClass[Int] {
    def doSomething(a: Int) = println(s"s$prefix $a")
  }
}

class MyLogic {

  def doSomething[A](a: A)(implicit myTypeClass: MyTypeClass[A]) = myTypeClass.doSomething(a)

  doSomething("Hello world")
}

TypeClasses实例中的隐式类型类实例转换为MyLogic的最佳方法是什么?

我唯一想到的就是 a)将TypeClasses的实例注入构造函数中的MyLogic,然后注入import instanceOfTypeClasses._。但是,这样做的缺点是每个类都必须重复此操作,并且子类不能继承导入。
或b)使TypeClasses为特征,prefix为def,并使MyLogic扩展TypeClasses,然后在构造函数中为prefix注入实例。但是,这变得很混乱,因为它允许TypeClasses的依赖项渗入MyLogic

1 个答案:

答案 0 :(得分:1)

如果您需要依赖注入(我可能会从您的标签中假设),则可以尝试将distageslides)DI框架用于Scala(免责声明:是作者)。

它支持类型类实例注入。

由于您的Typeclass实例是在TypeClasses类中动态创建的, 您必须在每个需要的类中添加一个TypeClasses构造函数参数 调用需要类型类实例的函数。 但是,您可以通过创建隐式来删除import typeclasses._样板 可以从TypeClasses对象提取实例的defs:

trait MyTypeClass[A] {
  def doSomething(a: A): Unit
}

object MyTypeClass {
  implicit def intFromTypeClasses(implicit typeClasses: TypeClasses): MyTypeClass[Int] = typeClasses.intTc
  implicit def stringFromTypeClasses(implicit typeClasses: TypeClasses): MyTypeClass[String] = typeClasses.stringTC
}

因为隐式def是在MyTypeClass的伴随对象中定义的,所以它们始终可用而无需任何导入。

然后,您应该在MyLogic中添加TypeClasses作为隐式参数,这将使隐式def可以从中提取实例。

class MyLogic(implicit typeClasses: TypeClasses) {

  def doSomething[A](a: A)(implicit myTypeClass: MyTypeClass[A]) = myTypeClass.doSomething(a)

  doSomething("Hello world")
  // same as doSomething("Hello world")(MyTypeClass.stringFromTypeClasses(typeClasses))
}

然后在distage中声明以下绑定

import distage._

class MyAppModule extends ModuleDef {
  make[String].from("myprefix")
  make[TypeClasses]
  make[MyLogic]
}

一切都已连接:

val ctx: Locator = Injector().produce(new MyAppModule)

implicit val typeClasses = ctx.get[TypeClasses]
ctx.get[MyLogic].doSomething("Hello world")