Scala:进行隐式转换A-> B适用于选项[A] - >选项[B]

Alb*_*ert 21 scala scala-option

我正在尝试编写一个函数,当它们以通用方式包装在Option中时,重用我对Object A - > Object B的隐式转换,以便Option [A] - > Option [B]转换也工作.

我想出的是:

implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] = from.map(conversion(_))
Run Code Online (Sandbox Code Playgroud)

当我将一个(..)分配给一个值而不是当我分配一个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.
Run Code Online (Sandbox Code Playgroud)

我真的没有看到第一次和第二次转换之间的区别.不知何故,它不会在后者中调用隐式转换.我想它与类型系统有关,但我还不知道它是怎么回事.有任何想法吗?-Albert(我在scala 2.9.1)

jsu*_*eth 13

这是线索:

scala> val fooOpt: Option[Bar] = Option(Foo(1))
fooOpt: Option[Bar] = Some(Bar(1))
Run Code Online (Sandbox Code Playgroud)

而另一个:

scala> implicit def foobar(x: String): Int = augmentString(x).toInt
foobar: (x: String)Int

scala> val y: Option[String] = Option(1)
y: Option[String] = Some(1)

scala> val y: Option[Int] = Option("1")
y: Option[Int] = Some(1)
Run Code Online (Sandbox Code Playgroud)

看起来像一个合法的奇怪的 bug.我会打开一个较小的测试用例并打开一个问题(或在JIRA中搜索一个).

作为旁白:

您可以使用某种类别理论来处理许多不同类型的"Option-ish"事物.

package object fun {
  trait Functor[Container[_]] {
    def fmap[A,B](x: Container[A], f: A => B): Container[B]
  }
  object Functor {
     implicit object optionFunctor extends Functor[Option] {
       override def fmap[A,B](x: Option[A], f: A => B): Option[B] = x map f
     }
     // Note: With some CanBuildFrom magic, we can support Traversables here.
  }
  implicit def liftConversion[F[_], A, B](x: F[A])(implicit f: A => B, functor: Functor[F]): F[B] = 
    functor.fmap(x,f)

}
Run Code Online (Sandbox Code Playgroud)

这有点先进,因为您将一些类别理论FP映射到问题上,但它是一种更通用的解决方案,可以根据需要将隐式对话提升到容器中.注意它们如何使用一个隐式会话方法链接,该方法采用更有限的隐式参数.

此外,这应该使示例工作:

scala> val tmp = Option(Foo(1))
tmp: Option[Foo] = Some(Foo(1))

scala> val y: Option[Bar] = tmp
y: Option[Bar] = Some(Bar(1))
Run Code Online (Sandbox Code Playgroud)

并使你的使用Some更危险:

scala> val tmp = Some(Foo(1))
tmp: Some[Foo] = Some(Foo(1))

scala> val y: Option[Bar] = tmp
<console>:25: error: could not find implicit value for parameter functor: fun.Functor[Some]
       val y: Option[Bar] = tmp
                            ^
Run Code Online (Sandbox Code Playgroud)

这告诉你方差是至关重要的,并且与隐含相互作用.我的猜测是你遇到了一个非常罕见的,可能很难修复的bug,可以使用其他技术来避免.


Dan*_*ral 13

你可能没有意识到这一点,但有一个标志:-Xlog-implicits.这就是它所说的:

scala> val barOpt2: Option[Bar] = fooOpt
fromOptionToOption is not a valid implicit value for Some[Foo] => Option[Bar] because:
incompatible: (from: Option[Foo])(implicit conversion: Foo => B)Option[B] does not match expected type Some[Foo] => Option[Bar]
<console>:16: error: type mismatch;
 found   : Some[Foo]
 required: Option[Bar]
       val barOpt2: Option[Bar] = fooOpt
                                  ^
Run Code Online (Sandbox Code Playgroud)

你去了 - 它不知道B必须是什么类型.0__提到使用不变集合不会发生这个问题,这是有道理的.在不变集合中,B必须完全相同Bar,而对于协变集合,它可以是任何子类型Bar.

那么,为什么val foo: Option[Foo] = Some(Bar(1))工作呢?嗯,有该标志太... -Ytyper-debug.然而,鉴于极端冗长,不适合弱者.

无论如何,我还是絮絮叨叨地比较了两种情况下发生的情况,答案很简单...... Option在这种情况下,并不是那个被转换的,但是Bar!请记住,您声明了一个隐式转换Bar => Foo,因此将结果传递给之前应用该转换Some!