scala - 如何避免具有多个类型类关系的模棱两可的转换链?

标签 scala typeclass context-bound

在我的图书馆中,我有三个类型类:

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] {
  ...
}

在以下条件下,这些类型类可以相互转换:
  • 如果输入 Tscala.math.Numeric类型,它也是AbelianGroup .
  • 如果输入 TAbelianGroup ,也是Monoid (目前, AbelianGroup 扩展了 Monoid ,但不一定是这种情况)
  • 如果输入 T是类型 U 上的向量,类型 U 是 Monoid ,然后输入 T也是Monoid .
  • 如果类型 T 是类型 U 上的向量,并且类型 U 是 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 等。所以,我觉得我在某个地方走错了路。

    有没有办法修改我的代码以避免这种冗余并解决编译时歧义错误?这种情况下的最佳实践是什么?

    最佳答案

    您可以 move the implicits for which you want to have lower priority into a supertype of the companion object :

    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/

    相关文章:

    scala - 使用 .ca-bundle、.crt 和 .key 文件为 Akka 服务器设置 https 支持

    haskell - 为什么 Num 不能与 0 进行比较?

    scala - 如何从TypeTag或同时获取ClassTag的ClassTag?

    c# - 原始类型的透明代理

    Scala 中的 LINQ 类似物?

    java - Akka:无法读取 karaf 中的配置

    scala - 具有继承类型的 Aux 模式推理失败

    haskell - 有什么方法可以自定义 GeneralizedNewtypeDeriving 实例的一种或两种方法?

    typeclass - Coq:展开类实例