scala - 具有继承类型的 Aux 模式推理失败

标签 scala types type-level-computation

我有一个复杂的玩具算法,我希望纯粹在类型级别上表示:根据饮食要求选择当天菜肴的修改。对卷积表示歉意,但我认为我们需要每一层才能达到我想要使用的最终界面。

我的代码有一个问题,如果我们对 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
}

这将每天改变膳食,独立于程序的其余部分。

ModifiedMeal 类型类,它接受膳食和饮食要求,并生成满足要求的副餐。子类型在这里很重要:

// <: 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中模式导致失败,或者我如何使用中间隐式构造程序来尝试解决问题?

最佳答案

尝试用证据替换泛型上的绑定(bind):

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
}

关于scala - 具有继承类型的 Aux 模式推理失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52660723/

相关文章:

performance - 在 Apache Spark 中广播外部库对象

scala - Map 不能在 scala 中序列化吗?

sql - 为什么使用rank()窗口函数会破坏解析器?

scala - 基于类的类型参数使用 Poly1 在方法中映射 HList

scala - scala 中意外的隐式解析

programming-languages - Scala 有什么了不起的?

types - Typed Racket Polymorphic Function 无法应用

javascript - 配置 Flow 以忽略 React Native 上的 "Missing type annotation for ..."错误

go - 在Go语句“typesubscriber struct {…}”中,type有什么作用?

haskell - 如何使用符号创建类型级别列表来测试类型系列