简体   繁体   English

在隐式搜索期间如何自动删除Scala特征?

[英]How can Scala traits be stripped automatically during implicit search?

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
}

(I get the same behaviour if I make Plentiful universal and/or add a self-type of Food to it.) (如果我做了Plentiful普遍和/或添加自我类型的Food ,我会得到相同的行为。)

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]]

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] . 在我看来,对F1是类型推断打破,因为ignoreSupply当编译器正在寻找一个根本就没有尝试过使用右类型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? 和/或以另一种方式实现ignoreSupply规则?

If you make IsHarmfulToEat contravariant the following code compiles 如果您使IsHarmfulToEat逆变,则以下代码编译

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM