无形文档解释了如何使用多态函数来创建一个函数,将一种容器中的对象映射到另一种容器,但是当你想从容器中解包东西时呢?
我有一个选项的HList
val options = Some(1) :: Some("A") :: Some(3.5) :: HNil
我想要一个可以提取每个选项内容的多态函数。
// This is incorrect:
object uuu extends (Option ~> Any) {
def apply[T](l:Option[T]):T = {
l.get
}
}
如果此功能正确,我想要以下行为:
options.map(uuu) // I want: 1 :: "A" :: 3.5 :: HNil
我如何纠正这个问题,以便我的多态函数真正起作用?
答案 0 :(得分:2)
这里有几个问题。首先,您的hlist的静态类型中包含Some
而不是Option
,因此需要证明Mapper
可以映射uuu
的{{1}}证据{1}}无法找到。解决此问题的最好方法是定义一个返回options
的智能Some
构造函数:
Option
您还可以向原始def some[A](a: A): Option[A] = Some(a)
val options = some(1) :: some("A") :: some(3.5) :: HNil
添加类型注释,或更改options
以使用uuu
代替Some
(这会更安全,但可能不太有用无论你想做什么,都是如此。
现在你的代码编译并执行某些东西,但这只是因为Option
在Scala中是多态的有些奇怪的事实。通常,当您拥有Any
时,F ~> G
和F
都必须是采用单一类型参数的类型构造函数,例如。 G
。 Option ~> List
没有采用类型参数,但它有效,因为有关Scala语言的这个奇怪的事实(Any
与Any
一起是类型多态的将适合您需要类型的任何插槽,带有一个参数的类型构造函数,带有十几个参数的类型构造函数等。)
所以它编译,但它没有用,因为它返回Nothing
。您可以使用Any :: Any :: Any :: HNil
替换自然转换中的Any
来解决此问题:
shapeless.Id
import shapeless._, shapeless.poly.~>
def some[A](a: A): Option[A] = Some(a)
val options = some(1) :: some("A") :: some(3.5) :: HNil
object uuu extends (Option ~> Id) {
def apply[T](l: Option[T]): T = l.get
}
options.map(uuu)
定义为Id
- 即,它是为您提供展开类型的标识类型构造函数。
这个版本既编译又返回了一个有用的类型结果,但它仍然不是很安全,因为如果你在type Id[+T] = T
上映射hlist
的元素(at运行时),你得到None
。除了将NoSuchElementException
更改为Option ~> Id
,以某种方式提供默认值等之外,还没有任何解决方法,所有这些都会极大地改变操作的性质。