Search code examples
scalagenericstype-inferenceimplicit-conversionimplicit

How can I create an inline function in scala that respects implicit conversions?


Take the following code as an example:

object Test6 {
  def main(args: Array[String]): Unit = {
    val a = new A
    //works
    takesBTuple(tupleMaker(a, a))
    //does not work, because a becomes an instance of ArrowAssoc[A] instead of ArrowAssoc[B]
    takesBTuple(a->a)
  }

  class A

  class B

  implicit def atob(a: A): B = new B

  def tupleMaker[X, Y](x: X, y: Y): (X, Y) = (x, y)

  def takesBTuple(b: (B, B)): Unit = println("taken")
}

How can I get the behavior of TupleMaker (specially w.r.t. implicit conversions), but with an inline function? I can modify both class A and B, and use any infix operator (doesn't have to be an existing one) if that helps the solution.


Solution

  • You can add one more conversion

    implicit def tupleToTuple(a: (A, A)): (B, B) = (atob(a._1), atob(a._2))
    

    More generic solution is

    class A
    class A1
    class B
    
    implicit def atob(a: A): B = new B
    implicit def a1tob(a: A1): B = new B
    
    implicit def tupleToTuple[T, X1, Y1, X, Y](t: (X, Y))(implicit 
      ev: T <:< (X1, Y1), 
      ev1: X => X1, 
      ev2: Y => Y1
    ): T = (t._1, t._2)
    
    
    val a = new A
    val a1 = new A1
    
    takesBTuple(a -> a) // compiles
    takesBTuple(a1 -> a1) // compiles
    takesBTuple(a -> a1) // compiles 
    

    Simpler

    implicit def tupleToTuple[X, Y, X1, Y1](t: (X, Y))(implicit 
      ev1: X => X1, 
      ev2: Y => Y1
    ): (X1, Y1) = (t._1, t._2) 
    

    doesn't work because X is inferred first (to be A). So ev: T <:< (X1, Y1) is a trick to make X1, Y1 be inferred first (to be B). Implicits are resolved from the left to the right.