У меня есть сложный алгоритм игрушек, который я хотел бы представить исключительно на уровне типа: выбор модификации блюда дня на основе требований к питанию.Извините за свертку, но я думаю, что нам нужен каждый слой, чтобы добраться до окончательного интерфейса, с которым я хочу работать.
Есть проблема с моим кодом, когда мы выражаем ограничение типа на 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
вызывает сбой, или как я мог бы структурировать программу с промежуточными последствиями, чтобы попытаться обойти проблему?