最近,Scala 博主们似乎对类型类模式,其中一个简单的类具有由符合某些特征或模式的附加类添加的功能。作为一个过于简单化的例子,简单的类:
case class Wotsit (value: Int)
可以适应 Foo 特征:
trait Foo[T] {
def write (t: T): Unit
}
在此类的帮助下:
implicit object WotsitIsFoo extends Foo[Wotsit] {
def write (wotsit: Wotsit) = println(wotsit.value)
}
类型类通常在编译时通过隐式捕获,允许将 Wotsit 及其类型类一起传递到高阶函数中:
def writeAll[T] (items: List[T])(implicit tc: Foo[T]) =
items.foreach(w => tc.write(w))
writeAll(wotsits)
(在你纠正我之前,我说过这是一个过于简单的例子)
However,隐式的使用假定在编译时已知项的精确类型。我发现在我的代码中通常情况并非如此:我将拥有某种类型的项目 List[T] 的列表,并且需要发现正确的类型类来处理它们。
Scala 建议的方法似乎是在调用层次结构中的所有点添加 typeclass 参数。当代码扩展时,这可能会变得烦人,并且这些依赖项需要通过它们越来越不相关的方法沿着越来越长的链传递。这使得代码变得混乱且难以维护,这与 Scala 的初衷相反。
通常,这是依赖注入介入的地方,使用库在需要时提供所需的对象。详细信息因为 DI 选择的库而异 - 我过去用 Java 编写了自己的库 - 但通常注入点需要精确定义所需的对象。
问题是,对于类型类,精确值在编译时是未知的。必须根据多态性描述来选择它。至关重要的是,类型信息已被编译器删除。清单是 Scala 的类型擦除解决方案,但我还不清楚如何使用它们来解决这个问题。
人们会建议使用哪些 Scala 技术和依赖注入库来解决这个问题?我错过了一个技巧吗?完美的 DI 库?或者这确实是症结所在?
澄清
我认为这实际上有两个方面。在第一种情况下,需要类型类的点是通过从已知其操作数的确切类型的点进行直接函数调用来到达的,因此足够的类型争论和语法糖可以允许将类型类传递给点它是需要的。
在第二种情况下,这两点被屏障分开 - 例如无法更改的 API,或存储在数据库或对象存储中,或序列化并发送到另一台计算机 - 这意味着类型类可以' t 与其操作数一起传递。在这种情况下,给定一个其类型和值仅在运行时已知的对象,需要以某种方式发现类型类。
我认为函数式程序员习惯于假设第一种情况——使用足够高级的语言,操作数的类型总是可知的。 David 和 mkniessl 对此提供了很好的答案,我当然不想批评这些。但第二种情况确实存在,这就是为什么我将依赖注入引入到问题中。