【问题标题】:Scala type classes with constructor dependency injection具有构造函数依赖注入的 Scala 类型类
【发布时间】:2018-10-17 21:51:52
【问题描述】:

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

例如,给定

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 设为 trait,prefix 设为 def,并让 MyLogic 扩展 TypeClasses,然后在构造函数中依赖注入 prefix 的实例。然而,这变得混乱,因为它允许TypeClasses 的依赖关系渗入MyLogic

【问题讨论】:

    标签: scala dependency-injection typeclass


    【解决方案1】:

    如果您需要依赖注入,正如我可能从您的标签中假设的那样,您可以尝试使用distage (slides) 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
    }
    

    因为隐式定义是在 MyTypeClass 的伴随对象中定义的,所以它们在没有任何导入的情况下始终可用。

    那么你应该在 MyLogic 中添加 TypeClasses 作为隐式参数,这将使其可用于隐式定义以从以下位置提取实例:

    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")
    

    【讨论】:

      猜你喜欢
      • 2020-10-24
      • 1970-01-01
      • 1970-01-01
      • 2011-09-02
      • 2013-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      相关资源
      最近更新 更多