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)
Implicit conversions in Scala are the set of methods that are apply when an object of wrong type is used. It allows the compiler to automatically convert of one type to another. Implicit conversions are applied in two conditions: First, if an expression of type A and S does not match to the expected expression type B.
An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.
When an implicit value is required, the Scala compiler would first look into the current scope to find the required implicit value that can act as a function for the type conversion required. The current scope used as implicit scope include: Local scope.
Scala 2.10 introduced a new feature called implicit classes. An implicit class is a class marked with the implicit keyword. This keyword makes the class's primary constructor available for implicit conversions when the class is in scope. Implicit classes were proposed in SIP-13.
Here's clue:
scala> val fooOpt: Option[Bar] = Option(Foo(1)) fooOpt: Option[Bar] = Some(Bar(1))
And another:
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)
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.
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) }
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:
scala> val tmp = Option(Foo(1)) tmp: Option[Foo] = Some(Foo(1)) scala> val y: Option[Bar] = tmp y: Option[Bar] = Some(Bar(1))
And make your usage of Some
more dangerous:
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 ^
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With