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

Using a polymorphic function to extract an object from Options

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

如何更正此问题以便我的多态函数真正起作用?

这里有几个问题。首先是你的 hlist 的静态类型有 Some 而不是 Option,所以你需要证明 uuuMapper 证据可以映射到 options 将找不到。解决这个问题的最好方法是定义一个智能 Some 构造函数,它 returns 一个 Option:

def some[A](a: A): Option[A] = Some(a)

val options = some(1) :: some("A") :: some(3.5) :: HNil

您还可以将类型注释添加到原始 options,或更改 uuu 以使用 Some 而不是 Option(这会更安全,但大概无论您打算做什么,都不太有用。

现在您的代码可以编译并执行 某些事情,但这只是因为 Any 在 Scala 中是种类多态性这一有点奇怪的事实。一般来说,当你有 F ~> G 时,FG 都必须是采用单个类型参数的类型构造函数——例如Option ~> ListAny 不接受类型参数,但它仍然有效,因为关于 Scala 语言的这个奇怪的事实(AnyNothing 是种类多态的并且适合任何你需要一个类型的插槽,一个带一个参数的类型构造函数,一个带一打参数的类型构造函数,等等)。

所以它可以编译,但是它没什么用,因为它 returns 和 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——即,它是为您提供未包装类型的身份类型构造函数。

这个版本既可以编译又可以返回一个有用类型的结果,但它仍然不是很安全,因为如果你映射一个 hlistNone 的元素(在运行时) ,你会得到一个 NoSuchElementException。除了将 Option ~> Id 更改为 Some ~> Id、以某种方式提供默认值等之外,没有任何解决方法,所有这些都极大地改变了操作的性质。