Шаблон Aux с унаследованными типами не может быть выведен - PullRequest
0 голосов
/ 05 октября 2018

У меня есть сложный алгоритм игрушек, который я хотел бы представить исключительно на уровне типа: выбор модификации блюда дня на основе требований к питанию.Извините за свертку, но я думаю, что нам нужен каждый слой, чтобы добраться до окончательного интерфейса, с которым я хочу работать.

Есть проблема с моим кодом, когда мы выражаем ограничение типа на Aux- сгенерированный типом паттернов, основанный на другом родовом типе, он не может определить тип.

Это блюда, в действительности было бы много разновидностей пиццы и много базовых блюд:

trait Pizza
trait CheeselessPizza extends Pizza

Диетические требования:

sealed trait DietaryRequirement
trait Vegan extends DietaryRequirement

Блюдо дневного класса:

sealed trait DishOfTheDay[Meal]

object DishOfTheDay {
  implicit val dishOfTheDay: DishOfTheDay[Pizza] = null
}

Это будет менять еду каждый день, независимо от остальной части программы.

AКласс типов ModifiedMeal, который принимает пищу и диетические требования и генерирует submeal , который удовлетворяет требованиям.Здесь важен подтип:

// <: Meal is important
sealed trait ModifiedMeal[Meal, D <: DietaryRequirement] { type Mod <: Meal }

object ModifiedMeal {

  type Aux[Meal, D <: DietaryRequirement, Mod0 <: Meal] = ModifiedMeal[Meal, D] { type Mod = Mod0 }

  // Only one instance so far, Vegan Pizza = CheeselessPizza
  implicit val veganPizzaModifiedMeal: ModifiedMeal.Aux[Pizza, Vegan, CheeselessPizza] = null

}

И вот наш последний класс типов, который делает за нас вычисления:

// Given a dietary requirement, give us a dish of the day which satisfies it
// if one exists
trait DishOfTheDayModification[Req <: DietaryRequirement] { type Out }

object DishOfTheDayModification {

  type Aux[Req <: DietaryRequirement, Out0] = DishOfTheDayModification[Req] { type Out = Out0 }

  // Find the dish of the day, then find a ModifiedMeal of it
  // <: Meal is important here so we pick up ONLY pizzas and not some other meal
  implicit def dishOfTheDayModification[Meal, Req <: DietaryRequirement, Mod <: Meal](
    implicit d: DishOfTheDay[Meal],
    impl: ModifiedMeal.Aux[Meal, Req, Mod]
  ): DishOfTheDayModification.Aux[Req, Mod] = null

}

А вот тестирование:

object MealTesting {
  def veganDishOfTheDay[Mod](implicit d: DishOfTheDayModification.Aux[Vegan, Mod]): Mod = ???

  // Does not compile but it should
  veganDishOfTheDay: CheeselessPizza
}

Проблема в том, что этот метод не компилируется, но он должен .

Если вы скопируете всю программу, но удалите требования <: Meal из сгенерированной еды, она скомпилируется.Здесь снова все, но «работает»:

trait Pizza
trait CheeselessPizza extends Pizza

sealed trait DietaryRequirement
trait Vegan extends DietaryRequirement

sealed trait DishOfTheDay[Meal]

object DishOfTheDay {
  implicit val dishOfTheDay: DishOfTheDay[Pizza] = null
}

sealed trait ModifiedMeal[Meal, D <: DietaryRequirement] { type Mod }

object ModifiedMeal {

  type Aux[Meal, D <: DietaryRequirement, Mod0] = ModifiedMeal[Meal, D] { type Mod = Mod0 }

  implicit val veganPizzaModifiedMeal: ModifiedMeal.Aux[Pizza, Vegan, CheeselessPizza] = null

}

trait DishOfTheDayModification[Req <: DietaryRequirement] { type Out }

object DishOfTheDayModification {

  type Aux[Req <: DietaryRequirement, Out0] = DishOfTheDayModification[Req] { type Out = Out0 }

  implicit def dishOfTheDayModification[Meal, Req <: DietaryRequirement, Mod](
    implicit d: DishOfTheDay[Meal],
    impl: ModifiedMeal.Aux[Meal, Req, Mod]
  ): DishOfTheDayModification.Aux[Req, Mod] = null

}

object MealTesting {
  def veganDishOfTheDay[Mod](implicit d: DishOfTheDayModification.Aux[Vegan, Mod]): Mod = ???

  // DOES compile
  veganDishOfTheDay: CheeselessPizza
}

Но мы не хотим этого, потому что это позволяет нам генерировать блюда, которые НЕ являются подтипом блюда дня.

Кто-нибудь знает, почему наследование в шаблоне Aux вызывает сбой, или как я мог бы структурировать программу с промежуточными последствиями, чтобы попытаться обойти проблему?

Ответы [ 2 ]

0 голосов
/ 06 октября 2018

Ваш первоначальный подход был очень близок к тому, чтобы не иметь проблем, и для его успешной компиляции с использованием Scala v2.12 требуется лишь незначительная корректировка подписи 1 dishOfTheDayModification.

Для справки, висходное DishOfTheDayModification определение объекта было таким:

// Find the dish of the day, then find a ModifiedMeal of it
// <: Meal is important here so we pick up ONLY pizzas and not some other meal
implicit def dishOfTheDayModification[Meal, Req <: DietaryRequirement, Mod <: Meal](
  implicit
     // vvvvvv - Here's the problem
     d: DishOfTheDay[Meal],
     impl: ModifiedMeal.Aux[Meal, Req, Mod]
  ): DishOfTheDayModification.Aux[Req, Mod] = null

Переключение порядка на:

implicit def dishOfTheDayModification[Meal, Req <: DietaryRequirement, Mod <: Meal](
  implicit
     impl: ModifiedMeal.Aux[Meal, Req, Mod],
     d: DishOfTheDay[Meal]
): DishOfTheDayModification.Aux[Req, Mod] = null

Позволяет компилятору успешно объединить Meal и Mod для impl до разрешения d.

0 голосов
/ 05 октября 2018

Попробуйте заменить связанное на дженерик доказательством:

trait Pizza
trait CheeselessPizza extends Pizza

sealed trait DietaryRequirement
trait Vegan extends DietaryRequirement

sealed trait DishOfTheDay[Meal]

object DishOfTheDay {
  implicit val dishOfTheDay: DishOfTheDay[Pizza] = null
}

sealed trait ModifiedMeal[Meal, D <: DietaryRequirement] { type Mod <: Meal }

object ModifiedMeal {
  type Aux[Meal, D <: DietaryRequirement, Mod0 /*<: Meal*/] = ModifiedMeal[Meal, D] { type Mod = Mod0 }

  //implicit val veganPizzaModifiedMeal: ModifiedMeal.Aux[Pizza, Vegan, CheeselessPizza] = null

  def mkAux[Meal, D <: DietaryRequirement, Mod](implicit ev: Mod <:< Meal): Aux[Meal, D, Mod] = null

  implicit val veganPizzaModifiedMeal: ModifiedMeal.Aux[Pizza, Vegan, CheeselessPizza] = mkAux
}

trait DishOfTheDayModification[Req <: DietaryRequirement] { type Out }

object DishOfTheDayModification {
  type Aux[Req <: DietaryRequirement, Out0] = DishOfTheDayModification[Req] { type Out = Out0 }

  implicit def dishOfTheDayModification[Meal, Req <: DietaryRequirement, Mod /*<: Meal*/](implicit 
    d: DishOfTheDay[Meal],
    impl: ModifiedMeal.Aux[Meal, Req, Mod],
    ev: Mod <:< Meal
  ): DishOfTheDayModification.Aux[Req, Mod] = null
}

object MealTesting {
  def veganDishOfTheDay[Mod](implicit d: DishOfTheDayModification.Aux[Vegan, Mod]): Mod = ???

  veganDishOfTheDay: CheeselessPizza
}
...