18

我正在尝试学习 Shapeless(使用 2.10.2 版)。我创建了一个非常简单的可扩展记录:

val rec1 = ("foo" ->> 42) :: HNil

根据 REPL,这有类型

shapeless.::[Int with shapeless.record.KeyTag[String("foo"),Int],shapeless.HNil]

我正在尝试定义一个简单的函数:

def fun(x: ::[Int with KeyTag[String("foo"), Int], HNil]) = x("foo")

但它甚至不编译。我不能在类型声明中使用 String("foo") 并得到一个错误。

我有两个问题:

  1. 如何在我的代码中指定可扩展记录的类型?
  2. 当处理具有更多字段的记录时,类型声明的长度和复杂性将难以管理。给定记录的特定实例或其他解决方法,有没有办法为该类型创建别名?

编辑

我发现:

val rec1 = ("foo" ->> 42) :: HNil
val rec2 = ("foo" ->> 43) :: HNil
var x = rec1
x = rec2

效果很好。我得出结论rec1、rec2 和x 属于同一类型。我只是不知道如何在代码中表达这种类型!

4

2 回答 2

27

这里有一些更笼统的东西,我认为可能会回答你的问题。假设我们要编写一个方法,该方法可以处理任何带有"foo"键的记录。我们可以使用见证人和选择器的组合:

import shapeless._, record._, syntax.singleton._

val w = Witness("foo")

def fun[L <: HList](xs: L)(implicit sel: ops.record.Selector[L, w.T]) = xs("foo")

接着:

scala> fun(("foo" ->> 42) :: HNil)
res0: Int = 42

或者:

scala> fun(("bar" ->> 'a) :: ("foo" ->> 42) :: HNil)
res1: Int = 42

如果我们真的想只允许没有其他字段的记录,我们可以编写以下内容:

def fun(l: Int with KeyTag[w.T, Int] :: HNil) = l("foo")

但这与通常使用记录的方式有些不一致。

我们必须准确地定义见证人,因为 Scala 2.10 没有提供任何直接引用单例类型的方法——例如,请参阅的 Alois Cochard 的Shona项目的 fork 进行一些讨论。

作为最后的免责声明,我要补充一点,我自己现在才刚刚熟悉 Shapeless 2.0,但我认为即使是 Miles 也没有足够的魔力来绕过这个限制。

于 2013-10-11T10:55:36.107 回答
6

从 shapeless 2.1.0 开始,有一种新的语法来表达记录类型:

scala> :paste
// Entering paste mode (ctrl-D to finish)

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

def fun(x: Record.`"foo" -> Int`.T) = x("foo")

// Exiting paste mode, now interpreting.

import shapeless._
import shapeless.record._
import shapeless.syntax.singleton._
fun: (x: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil])Int

scala> fun( ("foo" ->> 42) :: HNil )
res2: Int = 42

scala> fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil )
<console>:30: error: type mismatch;
 found   : shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.::[Int with shapeless.labelled.KeyTag[String("bar"),Int],shapeless.HNil]]
 required: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil]
       fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil )

但是选择器可能是 OP 用例的最佳方法。

于 2015-08-09T22:21:46.137 回答