我正在尝试学习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”),并且会出错。
我有两个问题:
修改
我发现:
val rec1 = ("foo" ->> 42) :: HNil
val rec2 = ("foo" ->> 43) :: HNil
var x = rec1
x = rec2
运作良好。我得出结论rec1,rec2和x属于同一类型。我只是不知道如何在代码中表达这种类型!
答案 0 :(得分:26)
我觉得这可能会回答你的问题。假设我们想要编写一个可以使用"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的my fork项目的Shona进行讨论。
我将添加作为最终免责声明,我现在才刚刚熟悉Shapeless 2.0,但我认为即使是Miles也不足以克服这个限制。
答案 1 :(得分:6)
从无形的2.1.0开始,有new syntax来表达记录类型:
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用例的最佳方法。