使用多态函数从Options中提取对象

时间:2016-09-21 23:28:51

标签: scala shapeless

无形文档解释了如何使用多态函数来创建一个函数,将一种容器中的对象映射到另一种容器,但是当你想从容器中解包东西时呢?

我有一个选项的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

我如何纠正这个问题,以便我的多态函数真正起作用?

1 个答案:

答案 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 ~> GF都必须是采用单一类型参数的类型构造函数,例如。 GOption ~> List没有采用类型参数,但它有效,因为有关Scala语言的这个奇怪的事实(AnyAny一起是类型多态的将适合您需要类型的任何插槽,带有一个参数的类型构造函数,带有十几个参数的类型构造函数等。)

所以它编译,但它没有用,因为它返回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,以某种方式提供默认值等之外,还没有任何解决方法,所有这些都会极大地改变操作的性质。