在我的图书馆中,我有三个类型类:
trait Monoid[T] {
val zero : T
def sum(x : T, y : T) : T
}
trait AbelianGroup[T] extends Monoid[T] {
def inverse(x : T) : T
def difference(x : T, y : T) : T
}
//represents types that are represents lists with a fixed number of elements, such as
//the tuple type (Int, Int)
trait Vector[T, U] {
...
}
在以下条件下,这些类型类可以相互转换:
T
是 scala.math.Numeric
类型,它也是AbelianGroup
. T
是 AbelianGroup
,也是Monoid
(目前, AbelianGroup
扩展了 Monoid
,但不一定是这种情况)T
是类型 U 上的向量,类型 U 是 Monoid
,然后输入 T
也是Monoid
. AbelianGroup
,然后 T
也是AbelianGroup
. 例如,由于
(Int, Int)
是一个 Vector over type Int
, 和 Int
是一个阿贝尔群,那么 (Int, Int)
也是一个阿贝尔群。这些关系和其他关系很容易在伴随类中实现,如下所示:
object Monoid {
implicit def fromAbelianGroup[T : AbelianGroup] : Monoid[T] = implicitly[AbelianGroup[T]]
implicit def fromVector[T : Vector[T, U], U : Monoid] : Monid[T] = ...
}
object AbelianGroup {
implicit def fromNumeric[T : Numeric] : AbelianGroup[T] = ...
implicit def fromOtherTypeX[T : ...] : AbelianGroup[T]
...
implicit def fromVector[T : Vector[T, U], U : AbelianGroup] : AbelianGroup[T] = ...
}
这很有效,直到您尝试使用元组类型
(Int, Int)
之类的东西。作为幺半群。编译器找到了两种方法来获取这种类型的 Monoid 类型类对象:Monoid.fromAbelianGroup(AbelianGroup.fromVector(Vector.from2Tuple,
AbelianGroup.fromNumeric))
Monoid.fromVector(Vector.from2Tuple,
Monid.fromAbelianGroup(AbelianGroup.fromNumeric))
为了解决这个歧义,我修改了 Monoid 伴随类,以包含从 Numeric 的直接转换(以及其他可直接转换为
AbelianGroup
的类型)。/*revised*/
object Monoid {
//implicit def fromAbelianGroup[T : AbelianGroup] : Monoid[T] = implicitly[AbelianGroup[T]]
implicit def fromNumeric[T : Numeric] : Monoid[T] = ... //<-- redundant
implicit def fromOtherTypeX[T : ...] : AbelianGroup[T] = ... //<-- redundant
...
implicit def fromVector[T : Vector[T, U], U : Monoid] : Monid[T] = ...
}
object AbelianGroup {
implicit def fromNumeric[T : Numeric] : AbelianGroup[T] = ...
implicit def fromOtherTypeX[T : ...] : AbelianGroup[T] = ...
...
implicit def fromVector[T : Vector[T, U], U : AbelianGroup] : AbelianGroup[T] = ...
}
然而,这有点令人不满意,因为它本质上违反了 DRY 原则。当我为
AbelianGroup
添加新的实现时s,我必须在两个伴随对象中实现转换,就像我对 Numeric
所做的一样。和OtherTypeX 等。所以,我觉得我在某个地方走错了路。有没有办法修改我的代码以避免这种冗余并解决编译时歧义错误?这种情况下的最佳实践是什么?
最佳答案
trait LowPriorityMonoidImplicits {
implicit def fromVector[T : Vector[T, U], U : Monoid] : Monoid[T] = ...
}
object Monoid extends LowPriorityMonoidImplicits {
implicit def fromAbelianGroup[T : AbelianGroup] : Monoid[T] = implicitly[AbelianGroup[T]]
}
关于scala - 如何避免具有多个类型类关系的模棱两可的转换链?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31691418/