私のライブラリには、次の 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
(現在AbelianGroup
extendsMonoid
ですが、必ずしもそうである必要はありません) - 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
この冗長性を回避し、コンパイル時のあいまいさエラーを解決するためにコードを修正する方法はありますか? この種のシナリオでのベスト プラクティスは何ですか?