7

私のライブラリには、次の 3 つの型クラスがあります。

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

これらの型クラスは、次の条件下で相互に変換できます。

  • typeTscala.math.Numeric型の場合、それはAbelianGroup.
  • typeTが の場合AbelianGroup、それもMonoid(現在AbelianGroupextendsMonoidですが、必ずしもそうである必要はありません)
  • typeTが型 U の Vector であり、型 U が aMonoidである場合、型Tも aMonoidです。
  • 型 T が型 U の Vector であり、型 U が であるAbelianGroup場合、TAbelianGroupです。

たとえば、(Int, Int)は type 上の Vector でIntありInt、AbelianGroup であるため、(Int, Int)も AbelianGroup です。

これらの関係やその他の関係は、次のようにコンパニオン クラスで簡単に実装できます。

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 型クラス オブジェクトを取得する 2 つの方法を見つけます。

  1. Monoid.fromAbelianGroup(AbelianGroup.fromVector(Vector.from2Tuple, AbelianGroup.fromNumeric))

  2. 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 の原則に違反しているため、少し満足のいくものではありません。s の新しい実装を追加するときは、と OtherTypeX などでAbelianGroup行ったのと同じように、両方のコンパニオン オブジェクトに変換を実装する必要があります。Numeric

この冗長性を回避し、コンパイル時のあいまいさエラーを解決するためにコードを修正する方法はありますか? この種のシナリオでのベスト プラクティスは何ですか?

4

1 に答える 1

0

優先度を下げたい暗黙オブジェクトをコンパニオン オブジェクトのスーパータイプに移動できます。

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]]
}
于 2015-10-29T13:25:32.907 に答える