私のライブラリには、次の 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] {
...
}
これらの型クラスは、次の条件下で相互に変換できます。
- type
Tがscala.math.Numeric型の場合、それはAbelianGroup. - type
Tが の場合AbelianGroup、それもMonoid(現在AbelianGroupextendsMonoidですが、必ずしもそうである必要はありません) - type
Tが型 U の Vector であり、型 U が aMonoidである場合、型Tも aMonoidです。 - 型 T が型 U の Vector であり、型 U が である
AbelianGroup場合、TもAbelianGroupです。
たとえば、(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 つの方法を見つけます。
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 の原則に違反しているため、少し満足のいくものではありません。s の新しい実装を追加するときは、と OtherTypeX などでAbelianGroup行ったのと同じように、両方のコンパニオン オブジェクトに変換を実装する必要があります。Numeric
この冗長性を回避し、コンパイル時のあいまいさエラーを解決するためにコードを修正する方法はありますか? この種のシナリオでのベスト プラクティスは何ですか?