Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: Implicit Conversion From Generic Type to Second Generic Type

Say I have two sets of classes and the first set inherits from Foo and the second set inherits from Bar.

class Foo
class Baz extends Foo
class Bar
class Qux extends Bar

I want to make a generic implicit conversion function that converts any Foo to Bar given there is an implicit converter type in scope.

trait Converter[A <: Foo, B <: Bar] {
  def convert(a : A) : B
}

implicit object BazToQuxConverter extends Converter[Baz, Qux] {
  def convert(a : Baz) : Qux = new Qux
}

implicit def FooToBar[A <: Foo, B <: Bar](a : A)(implicit converter : Converter[A, B]) : B = converter.convert(a)

Unfortunately, this doesn't seem to work as I would expect it to. When I plug in the following lines to the REPL:

val a : Baz = new Baz
val b : Qux = a

...I get the following error:

<console>:17: error: type mismatch;
 found   : Baz
 required: Qux
       val b : Qux = a
                     ^

Is there any way to get this to work? The closest I've been able to come is the following:

implicit def BadFooToBar[A <: Foo, B <: Bar](a : A)(implicit converter : Converter[A, _]) : B = converter.convert(a).asInstanceOf[B]

This does work for my previous example, but it's not very type-safe.

class Qax extends Bar
val a : Baz = new Baz
val b : Qax = a

This will compile just fine, but it will blow up at run-time because a Qux (the result of converter.convert(a)) can't be cast to Qax (asInstanceOf[Qax]). Ideally, I want it so that the above line will be caught at compile time since there isn't any Converter[Bax,Qax] in scope.

like image 622
LambdaKnight Avatar asked Nov 04 '13 20:11

LambdaKnight


1 Answers

This is a bug which is fixed in 2.11. It looks like it has been fixed since PR 2822, the related ticket is SI-3346.

Welcome to Scala version 2.11.0-20131030-090728-c38235fd44 (OpenJDK 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :paste
// Entering paste mode (ctrl-D to finish)

  class Foo
  class Baz extends Foo
  class Bar
  class Qux extends Bar

  trait Converter[A <: Foo, B <: Bar] {
    def convert(a : A) : B
  }

  implicit object BazToQuxConverter extends Converter[Baz, Qux] {
    def convert(a : Baz) : Qux = new Qux
  }

  import scala.language.implicitConversions
  implicit def FooToBar[A <: Foo, B <: Bar](a : A)(implicit converter : Converter[A, B]) : B = converter.convert(a)

  val a : Baz = new Baz
  val b : Qux = a

// Exiting paste mode, now interpreting.

defined class Foo
defined class Baz
defined class Bar
defined class Qux
defined trait Converter
defined object BazToQuxConverter
import scala.language.implicitConversions
FooToBar: [A <: Foo, B <: Bar](a: A)(implicit converter: Converter[A,B])B
a: Baz = Baz@4f4db2ac
b: Qux = Qux@760d62e0
like image 134
kiritsuku Avatar answered Nov 04 '22 23:11

kiritsuku