使用无形Mapper而无需指定结果类型

时间:2016-08-10 19:51:57

标签: scala shapeless type-level-computation

对于习惯于Scala类型级编程的开发人员来说,这似乎是一个经典问题,但我找不到(或者我不知道如何搜索)解决方案或模式。假设我有这样一个类:

abstract class TypedTest[Args <: HList](implicit val optMapper: Mapped[Args, Option]) {
  type OptArgs = optMapper.Out

  def options: OptArgs // to be implemented by subclasses
}

我希望此类的用户使用HList类型参数(Args)对其进行实例化,并且该类提供了一种方法来检索包含每个指定类型的实例的HList实例在OptionOptArgs)内。我正在使用无形Mapped类型类。请注意,我在实例化时提供Args的实例。

此代码不起作用,因为编译器不会推断OptArgs的具体类型,甚至明显正确的实现(例如def options = HNil)也会产生编译错误。使用Aux模式的相同代码:

abstract class TypedTest[Args <: HList, OptArgs <: HList](implicit val optMapper: Mapped.Aux[Args, Option, OptArgs]) {
  def options: OptArgs
}

这迫使我在实例化时指定两个列表,这使得外部API不必要地冗长。有解决方法吗?

1 个答案:

答案 0 :(得分:0)

这是我的理解,但我并不是百分百肯定,并且很乐意接受纠正。

类型成员TypedTest.OptArgs不是抽象类型,而是类型别名。它是TypedTest的所有子类的相同类型 - Mapped[Args, Option].Out的别名,它是一种抽象类型,不能与任何类型统一,只能与其本身统一。创建子类时,不会覆盖类型成员OptArgs

Mapped.Aux使用Out0的存在类型时,它变得更加清晰,这与IIUC或多或少相同:

abstract class TypedTest[Args <: HList](
    implicit val optMapper: Mapped.Aux[Args, Option, T] forSome { type T }) {

  type OptArgs = optMapper.Out

  def options: OptArgs // to be implemented by subclasses
}

val intTest = new TypedTest[Int :: HNil] {
  def options = Some(1) :: HNil
}

Error:(18, 29) type mismatch;
found   : shapeless.::[Some[Int],shapeless.HNil]
required: this.OptArgs
  (which expands to)  T
    def options = Some(1) :: HNil

不幸的是,除了将Out作为类型参数添加或将OptArgs定义为抽象类型并在每个子类中明确指定它之外,我不知道任何可能的解决方案。