强化无形记录的价值

时间:2018-11-26 11:38:09

标签: scala generic-programming shapeless

我有一个无形记录的包装纸。

我想从该记录中提取一个值,并证明它是多态类型的实例,例如List[_]

import shapeless._
import shapeless.record._
import shapeless.ops.record._
import shapeless.syntax.singleton._

case class All[L <: HList](containers: L) {


  def getValue[Value, A](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, Value],
                         equiv: Value =:= List[A]
                        ): List[A] =
    equiv.apply(containers.get(containerKey))

}

现在,如果我明确指定类型参数getValueValue,则可以调用A,但是由于我使用的类型要比List更复杂,我确实需要推断出这些类型参数。

val all = All(
  'x ->> List[Int](1, 2, 3) ::
  'y ->> List[String]("a", "b") ::
  'z ->> 90
  HNil
)

// doesn't compile: Cannot prove that Value =:= List[A].
all.getValue('x)

// compiles
all.getValue[List[Int], Int]('x)

是否有一种方法可以提取值,将其强制转换为List[_],而不必指定任何类型参数?

请注意,如果我要证明值是简单的单态类型,例如Value =:= Int,而不是Value =:= List[A]

1 个答案:

答案 0 :(得分:1)

至少有两种方法可以完成此操作,并且都涉及更改getValue的签名。

首先,您可以只使用一个受约束的通用参数:

  def getValue[R <: List[_]](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, R]): R =
    containers.get(containerKey)

请注意,因为我们使用R作为返回类型,所以关于结果的编译时信息不会丢失。如果containerKey的值不是列表,您将无法调用此函数。


第二,您可以使用子类型范围。我不知道为什么会这样。我怀疑使用过严格的约束会导致编译器忽略某些使用优化类型的解决方案。如果您将Selector中的type参数替换为有界通配符,则这既可行:

  def getValue[A](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, _ <: List[A]]): List[A] =
    containers.get(containerKey)

或者,如果您使用子类型证据<:<而不是等式=:=

  def getValue[Value, A](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, Value],
                         equiv: Value <:< List[A]
                        ): List[A] =
    equiv.apply(containers.get(containerKey))