Scala:使隐式转换 A->B 适用于 Option[A] ->选项[B]
我正在尝试编写一个函数,该函数重新使用对象 A -> 的隐式转换。对象 B 当它们以通用方式包装在 Option 中时,使得 Option[A] ->选项[B] 转换也有效。
我想出的是:
implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] = from.map(conversion(_))
当我将 Some(..) 分配给一个值时,这有效,但当我分配 Option val 时,这不起作用;请参阅以下控制台输出:
scala> trait T
defined trait T
scala> case class Foo(i: Int) extends T
defined class Foo
scala> case class Bar(i: Int) extends T
defined class Bar
scala> implicit def fromFooToBar(f: Foo):Bar = Bar(f.i)
fromFooToBar: (f: Foo)Bar
scala> implicit def fromBarToFoo(b: Bar):Foo = Foo(b.i)
fromBarToFoo: (b: Bar)Foo
scala> implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] = from.map(conversion(_))
fromOptionToOption: [A, B](from: Option[A])(implicit conversion: (A) => B)Option[B]
scala> val foo: Option[Foo] = Some(Bar(1))
foo: Option[Foo] = Some(Foo(1))
// THIS WORKS as expected
scala> val fooOpt = Some(Foo(4))
fooOpt: Some[Foo] = Some(Foo(4))
scala> val barOpt2: Option[Bar] = fooOpt
<console>:16: error: type mismatch;
found : Some[Foo]
required: Option[Bar]
val barOpt2: Option[Bar] = fooOpt
^
//THIS FAILS.
我并没有真正看到第一次和第二次转换之间的区别。不知何故,它不会调用后者的隐式转换。我想这与类型系统有关,但我还不知道是怎么回事。有什么想法吗? -阿尔伯特 (我使用的是 scala 2.9.1)
I'm trying to write a function which re-uses the implicit conversions which I have for Object A -> Object B when they are wrapped in an Option in a generic way so that Option[A] -> Option[B] conversions also work.
What I've come up with is:
implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] = from.map(conversion(_))
This works when I assign a Some(..) to a value but not when I assign an Option val; see the following console output:
scala> trait T
defined trait T
scala> case class Foo(i: Int) extends T
defined class Foo
scala> case class Bar(i: Int) extends T
defined class Bar
scala> implicit def fromFooToBar(f: Foo):Bar = Bar(f.i)
fromFooToBar: (f: Foo)Bar
scala> implicit def fromBarToFoo(b: Bar):Foo = Foo(b.i)
fromBarToFoo: (b: Bar)Foo
scala> implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] = from.map(conversion(_))
fromOptionToOption: [A, B](from: Option[A])(implicit conversion: (A) => B)Option[B]
scala> val foo: Option[Foo] = Some(Bar(1))
foo: Option[Foo] = Some(Foo(1))
// THIS WORKS as expected
scala> val fooOpt = Some(Foo(4))
fooOpt: Some[Foo] = Some(Foo(4))
scala> val barOpt2: Option[Bar] = fooOpt
<console>:16: error: type mismatch;
found : Some[Foo]
required: Option[Bar]
val barOpt2: Option[Bar] = fooOpt
^
//THIS FAILS.
I don't really see the difference between the first and second conversion. Somehow it doesn't invoke the implicit conversion in the latter. I guess it has something to do with the type system, but I can't see how just yet. Any ideas?
-Albert
(I'm on scala 2.9.1)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这是线索:
还有另一个:
看起来像是一个合法的奇怪错误。我会弹出一个较小的测试用例并打开一个问题(或在 JIRA 中搜索一个问题)。
顺便说一句:
您可以使用一些范畴论来处理许多不同类型的“Option-ish”事物。
这有点更高级,因为您将某些类别理论 FP 映射到问题上,但它是根据需要将隐式对话提升到容器中的更通用的解决方案。请注意它们如何通过使用一种隐式对话方法进行链接,该方法采用更有限的隐式参数。
另外,这应该使示例正常工作:
并使您对
Some
的使用更加危险:这告诉您方差至关重要,并且与隐式交互。我的猜测是,您遇到了一个非常罕见的、可能难以修复的错误,可以使用其他技术来避免。
Here's clue:
And another:
Looks like a legitimately odd bug. I'd pop open a smaller test case and open an issue (or search for one in JIRA).
As an aside:
You could use some category theory to handle lots of different types of "Option-ish" things.
That's a bit more advanced, as you're mapping some category theory FP onto the problem, but it's a more general solution to lift implicit conversations into containers as needed. Notice how they chain by using one implicit conversation method that takes a more limited implicit argument.
ALSO, this should make the examples work:
And make your usage of
Some
more dangerous:That's telling you that variance is critical, and interacts with implicits. My guess is you ran into a very rare, probably hard to fix bug that can be avoided using other techniques.
您可能没有意识到这一点,但有一个标志:
-Xlog-implicits
。这就是它所说的:就这样——它不知道
B
必须是什么类型。 0__ 提到这个问题不会发生在不变集合上,这是有道理的。在不变集合中,B
必须完全是Bar
,而对于协变集合,它可以是Bar
的任何子类型。那么,为什么
val foo: Option[Foo] = Some(Bar(1))
有效呢?好吧,也有一个标志...-Ytyper-debug
。然而,鉴于极其冗长,不适合弱者。无论如何,我摇摇晃晃地通过,比较了两种情况下发生的情况,答案相当简单......在这种情况下转换的不是
Option
,而是Bar
!请记住,您声明了Bar => 的隐式转换; Foo
,因此它在将结果传递给Some
之前应用该转换!You might not be aware of it, but there's a flag for that:
-Xlog-implicits
. And this is what it says:And there you go -- it doesn't know what type
B
must be. 0__ mentioned that this problem doesn't happen with invariant collections, and that makes some sense. In invariant collections,B
must be exactlyBar
, while for covariant collections it could be any subtype ofBar
.So, why does
val foo: Option[Foo] = Some(Bar(1))
work? Well, there's a flag for that too...-Ytyper-debug
. Not for the weak, however, given the extreme verbosity.I waddled through anyway, comparing what happens in both cases, and the answer is rather simple... it's not the
Option
that is being converted in that case, butBar
! Remember, you declared an implicit conversion fromBar => Foo
, so it is applying that conversion before passing the result toSome
!它不起作用,因为 Scala 语言规范定义视图如下:
编译器似乎找不到目标和源都具有泛型类型的转换器。fromOptionToOption
不符合这三个类别,因为它采用隐式参数。定义从
Option[Foo]
到Option[Bar]
的视图按预期工作。运行此命令会打印出:
然而,一切并没有丢失。它不像一般的
Option
到Option
那么好,但是我们可以做任何可以变成Bar
到Option[Bar 的事情]
通过视图绑定。这是另一种解决方法,可用于一般
Option
到Option
,但需要额外的.convert
调用:It doesn't work because the Scala Language Specification defines view as follows:
Compiler doesn't seem to find converter with both destination and source having generic type.fromOptionToOption
doesn't conform to the three categories since it takes an implicit parameter.Defining a view from
Option[Foo]
toOption[Bar]
works as expected.Running this prints out:
However, all is not lost. It's not as nice as general
Option
toOption
, but we can do something like anything that can turn intoBar
toOption[Bar]
by view bound.Here's another workaround that can be used for general
Option
toOption
but requires extra.convert
call:确实这是一个非常奇怪的问题。我尝试使用
Option
之外的其他类型,结果发现问题在于Option
在其类型参数中是协变的。这适用于所有情况:但是如果我将
A
定义为情况 (2) 失败。情况 (1) 总是成功,因为 Scala 在将
X
包装在A
中之前已经将其转换为Y
。现在我们知道了问题的来源,你需要等待类型大师来解释为什么这实际上是一个问题......转换仍然有效,你看:
Indeed it's a very strange problem. I tried to use another type than
Option
, and it turns out that the problem is thatOption
is covariant in its type parameter. This works all:But if instead I define
A
asThe case (2) fails. Case (1) always succeeds, because Scala already converts
X
toY
before wrapping it in anA
.Now that we know the problem source, you need to wait for a type guru to explain why this is actually a problem... The conversion is still valid, you see:
我改进了 @jseureth 答案 并添加了对
Traversable
的支持:现在您可以隐式转换选项和可遍历对象:
I improved @jseureth answer and added support for
Traversable
:Now you can implicitly convert options and traversables: