Как автоматически убрать черты Scala во время неявного поиска? - PullRequest
0 голосов
/ 21 февраля 2019

Я работаю с классами типов, и у меня возникают проблемы с автоматическим выводом их для типов, которые не связаны с дополнительными чертами (маркером / индикатором).Это трудно объяснить, но этот минимальный пример должен прояснить, что я имею в виду:

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

(я получаю такое же поведение, если я сделаю Plentiful универсальным и / или добавлю собственный тип Food к нему.)

Исследуя неявный журнал из компиляции, я нахожу это:

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 ломается, как ignoreSupplyпросто не проверяется с использованием правильных типов, когда компилятор ищет IsHarmfulToEat[Cake with Plentiful].Может кто-нибудь объяснить мне, почему это?И / или как направить компилятор, чтобы попробовать правильный тип?И / или для достижения правила ignoreSupply по-другому?

1 Ответ

0 голосов
/ 21 февраля 2019

Если вы сделаете 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]]
  }
...