问题描述
我正在使用类型类,但对于将它们无关地扩展额外的(标记/指示符)特征的类型自动导出它们时遇到了问题.很难解释,但是这个最小的例子应该清楚我的意思:
I am working with type classes and have problems auto-deriving them for types that unrelatedly extends extra (marker/indicator) traits. It is hard to explain, but this minimal example should make it clear what I mean:
// Base type we are working on
trait Food {}
// Marker trait - unrelated to Food's edibility
trait Plentiful {}
// Indicator type class we want to derive
trait IsHarmfulToEat[F<:Food] {}
object IsHarmfulToEat {
// Rule that says that if some food is harmful to eat,
// an enormous amount is so as well
implicit def ignoreSupply[F1<:Food,F2<:F1 with Plentiful]
(implicit isHarmful: IsHarmfulToEat[F1],
constraint: F2=:=F1 with Plentiful): IsHarmfulToEat[F2] =
new IsHarmfulToEat[F2]{}
}
// Example of food
case class Cake() extends Food {}
object Cake {
// Mark Cake as being bad for you
implicit val isBad: IsHarmfulToEat[Cake] = new IsHarmfulToEat[Cake] {}
}
object FoodTest extends App {
// Our main program
val ignoreSupplyDoesWork: IsHarmfulToEat[Cake with Plentiful] =
IsHarmfulToEat.ignoreSupply[Cake,Cake with Plentiful] // compiles fine
val badCake = implicitly[IsHarmfulToEat[Cake]] // compiles fine
val manyBadCakes = implicitly[IsHarmfulToEat[Cake with Plentiful]]
// ^^^ does not compile - I do not understand why
}
(如果我使 Purtiful
通用并且/或者向其中添加 Food
的自类型,我将得到相同的行为.)
(I get the same behaviour if I make Plentiful
universal and/or add a self-type of Food
to it.)
研究编译中的隐式日志,我发现:
Investigating the implicit-log from compilation, I find this:
Food.scala:33: util.this.IsHarmfulToEat.ignoreSupply is not a valid implicit value for IsHarmfulToEat[F1] because:
hasMatchingSymbol reported error: diverging implicit expansion for type IsHarmfulToEat[F1]
starting with method ignoreSupply in object IsHarmfulToEat
val manyBadCakes = implicitly[IsHarmfulToEat[Cake with Plentiful]]
^
Food.scala:33: util.this.IsHarmfulToEat.ignoreSupply is not a valid implicit value for IsHarmfulToEat[Cake with Plentiful] because:
hasMatchingSymbol reported error: diverging implicit expansion for type IsHarmfulToEat[F1]
starting with method ignoreSupply in object IsHarmfulToEat
val manyBadCakes = implicitly[IsHarmfulToEat[Cake with Plentiful]]
^
Food.scala:33: diverging implicit expansion for type IsHarmfulToEat[Cake with Plentiful]
starting with method ignoreSupply in object IsHarmfulToEat
val manyBadCakes = implicitly[IsHarmfulToEat[Cake with Plentiful]]
在我看来,F1的类型推断正在中断,因为在编译器寻找 IsHarmfulToEat [Cake with Plentiful]时,没有正确地尝试使用
ignoreSupply
.谁能向我解释为什么?和/或如何引导编译器尝试正确的类型?和/或以另一种方式实现ignoreSupply规则?
It seems to me that type inference on F1 is breaking down, as ignoreSupply
is simply not tried out using the right types when the compiler is looking for a IsHarmfulToEat[Cake with Plentiful]
. Can anyone explain to me why that is? And/or how to guide the compiler to try the right type? And/or to achieve the ignoreSupply rule in another way?
推荐答案
如果使 IsHarmfulToEat
与下面的代码相反,则会编译以下代码
If you make IsHarmfulToEat
contravariant the following code compiles
trait Food
trait Plentiful
trait IsHarmfulToEat[-F <: Food]
object IsHarmfulToEat {
implicit def ignoreSupply[F <: Food]
(implicit isHarmful: IsHarmfulToEat[F]
): IsHarmfulToEat[F with Plentiful] =
new IsHarmfulToEat[F with Plentiful]{}
}
case class Cake() extends Food {}
object Cake {
implicit val isBad: IsHarmfulToEat[Cake] = new IsHarmfulToEat[Cake] {}
}
object FoodTest extends App {
val ignoreSupplyDoesWork: IsHarmfulToEat[Cake with Plentiful] =
IsHarmfulToEat.ignoreSupply[Cake]
val badCake = implicitly[IsHarmfulToEat[Cake]]
val manyBadCakes = implicitly[IsHarmfulToEat[Cake with Plentiful]]
}
这篇关于隐式搜索期间如何自动剥离Scala特性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!