如何从Scala HList中读取元素?

4

关于HLists的易读文档非常少,我在SO上找到的答案对于一个Scala初学者来说太过复杂。

我遇到了HLists是因为Slick可以自动生成一些用于表示数据库行的HLists。它们是slick.collection.heterogeneous.HList(不是shapeless')。例如:

type MyRow = HCons[Int,HCons[String,HCons[Option[String],HCons[Int,HCons[String,HCons[Int,HCons[Int,HCons[Option[Int],HCons[Option[Float],HCons[Option[Float],HCons[Option[String],HCons[Option[String],HCons[Boolean,HCons[Option[String],HCons[Option[String],HCons[Option[String],HCons[Option[String],HCons[Option[String],HCons[Option[Int],HCons[Option[Float],HCons[Option[Float],HCons[Option[Float],HCons[Option[String],HCons[Option[String],HNil]]]]]]]]]]]]]]]]]]]]]]]]
def MyRow(a, b, c, ...): MyRow = a :: b :: c :: ... :: HNil

现在,如果给定其中的一行,我需要读取一个元素,并尽可能地进行类型转换。但我无法做到这一点。我已经尝试过了。

row(4)  // error
row._4  // error
row.toList  // elements are inferred as Any
row match { case a :: b :: c :: x :: rest => x }  // "Pattern type is incompatible. Expected MyRow."
row match { case MyRow(_,_,_,_,_,x,...) => x }  // is not a case class like other rows
row match { HCons[Int,HCons[String,HCons[Option[String],HCons[Int,HCons[String, x]]]]] => x.head }  // error
row.tail.tail.tail.tail.head  // well, is that really the way??

请问如何从那个“恐龙”中提取特定值?


似乎你对 HList 非常害怕,就像面对恐龙一样。 - sarveshseri
3
@SarveshKumarSingh 没有必要发表反映对你不了解的人品格的意见。我能建议你删除那条评论吗? - maasg
2个回答

1
我希望你的row(0)查找可以根据HList API文档中的apply工作。这是我在Slick 3.1.1上尝试的一个示例:
scala> import slick.collection.heterogeneous._
import slick.collection.heterogeneous._

scala> import slick.collection.heterogeneous.syntax._
import slick.collection.heterogeneous.syntax._

scala> type MyRow = Int :: String :: HNil
defined type alias MyRow

scala> val row: MyRow = 1 :: "a" :: HNil
row: MyRow = 1 :: a :: HNil

scala> row(0) + 99
res1: Int = 100

scala> val a: String = row(1)
a: String = a

我尝试使用这个新的导入,对我来说 row(0) 的类型是 Any,即使 row.head 的类型是 Int - JulienD
哦等等,IntellIJ将其标记为错误(“Any不符合预期的类型Int”),但它实际上确实可以编译。虽然我不明白这是如何可能的。 - JulienD
1
两件事情:row(0)的结果是一个类型别名,最终是Int,这可能会让IntelliJ感到困惑。另外:在确定这一点时涉及到宏,这可能是IntelliJ混淆的源头 - 我不知道 :-( 另外,如果您使用运行时值(例如val n=0; row(n)而不是像row(0)这样的文字),我认为您最终会得到Any - Richard Dallaway
我明白了。还有一件事:你知道为什么这个解决方案 val a: String = row.drop(1).head 不起作用吗?(“HList#Head does not conform to expected type String” - 这次编译失败了)。 - JulienD
抱歉,我不知道。我建议在Stack Overflow上开一个新问题来询问。 - Richard Dallaway

0

只有一件事...如果不是非常重要,那就坚持使用HList作为type。除非必要,不要将其别名为MyRow

所以..你有

val row = a :: b :: c :: ... :: HNil

这个怎么样?
val yourX = row match { case a :: b :: c :: x ::: rest => x }

请注意,在结尾处使用:::而不是::
或者...这样怎么样,
val yourX = row.tail.tail.tail.head

// this may change a little if you had,
def MyRow(a, b, c, ...): MyRow = a :: b :: c :: ... :: HNil

val row = MyRow(a, b, c, ...)

val yourX = row.asInstanceOf[HList].tail.tail.tail.head

Sbt显示“无法解析符号':::'”。目前我使用的是第二种解决方案,想象一下我想要的元素是12、14、15、17、26和27...但如果没有其他解决方案,我会保留它。 - JulienD
这个":::"确实在scaladocs中,但我无法在模式匹配中使用它。应该有一种方法可以使用.drop而不是.tail.tail.tail...,但我也无法使其工作。不过还是谢谢你的回答。 - JulienD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接