使用多态函数从选项中提取对象 [英] Using a polymorphic function to extract an object from Options
问题描述
the shapeless 文档解释了如何使用多态函数来创建一个函数,将一种容器中的对象映射到另一种容器,但是当您想从容器中解包东西时怎么办?
The shapeless documentation explains how to use polymorphic functions to make a function that maps objects in one kind of container to another, but what about when you want to unpack things from their container?
我有一个 HList 选项
I have a HList of Options
val options = Some(1) :: Some("A") :: Some(3.5) :: HNil
我想要一个可以提取每个选项内容的多态函数.
I want a polymorphic function that can extract the contents of each of the Options.
// This is incorrect:
object uuu extends (Option ~> Any) {
def apply[T](l:Option[T]):T = {
l.get
}
}
如果这个函数是正确的,我想要以下行为:
If this function was correct, I'd want the following behavior:
options.map(uuu) // I want: 1 :: "A" :: 3.5 :: HNil
我该如何纠正这个问题,以便我的多态函数真正起作用?
How can I correct this so that my polymorphic function actually works?
推荐答案
这里有几个问题.首先是你的 hlist 的静态类型有 Some
而不是 Option
,所以 Mapper
证明你需要证明 uuu
可以映射到 options
不会被发现.解决这个问题的最好方法是定义一个智能的 Some
构造函数,它返回一个 Option
:
There are a couple of problems here. The first is that the static type of your hlist has Some
in it instead of Option
, so the Mapper
evidence that you need to prove that uuu
can be mapped over options
won't be found. The nicest way to fix this is to define a smart Some
constructor that returns an Option
:
def some[A](a: A): Option[A] = Some(a)
val options = some(1) :: some("A") :: some(3.5) :: HNil
您还可以在原始 options
中添加类型注释,或更改 uuu
以使用 Some
而不是 Option代码>(这会更安全,但对于您的目标可能不太有用).
You could also add type annotations to your original options
, or change uuu
to work with Some
instead of Option
(which would be safer, but presumably less useful for whatever it is you're aiming to do).
现在您的代码可以编译并执行某些事情,但这仅仅是因为 Any
在 Scala 中是种类多态的这一有点奇怪的事实.一般来说,当你有 F ~>G
,F
和 G
都必须是采用单个类型参数的类型构造函数——例如<代码>选项~>列表.Any
不接受类型参数,但它可以工作,因为关于 Scala 语言的这个奇怪的事实(Any
,连同 Nothing
>, 是种类多态的,适合任何需要类型、带一个参数的类型构造函数、带十几个参数的类型构造函数等的插槽.
Now your code compiles and does something, but only because of the somewhat bizarre fact that Any
is kind-polymorphic in Scala. In general when you have F ~> G
, both F
and G
have to be type constructors that take a single type parameter—e.g. Option ~> List
. Any
doesn't take a type parameter, and yet it works, because of this weird fact about the Scala language (that Any
, together with Nothing
, is kind-polymorphic and will fit any slot where you need a type, a type constructor with one parameter, a type constructor with a dozen parameters, etc.).
所以它编译了,但它很没用,因为它返回一个Any :: Any :: Any :: HNil
.您可以通过将自然变换中的 Any
替换为 shapeless.Id
来解决此问题:
So it compiles, but it's pretty useless, since it returns an Any :: Any :: Any :: HNil
. You can fix this by replacing the Any
in the natural transformation with 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
——也就是说,它是为您提供解包类型的标识类型构造函数.
Id
is defined as type Id[+T] = T
—i.e., it's the identity type constructor that gives you the unwrapped type.
这个版本既编译又返回一个有用的类型结果,但它仍然不是真正安全的,因为如果你用 None
的元素映射 hlist
(在运行时),你会得到一个 NoSuchElementException
.除了改变 Option ~>Id
到 Some ~>Id
,以某种方式提供默认值等,所有这些都非常显着地改变了操作的性质.
This version both compiles and gives you back a usefully-typed result, but it's still not really safe, since if you map over an hlist
with elements that are None
(at runtime), you'll get a NoSuchElementException
. There's not really any way around this apart from changing Option ~> Id
to Some ~> Id
, somehow providing default values, etc., all of which change the nature of the operation pretty dramatically.
这篇关于使用多态函数从选项中提取对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!