Reputation: 161
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.
Upvotes: 0
Views: 294
Reputation: 51703
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.
Upvotes: 1
Reputation: 7604
The easy way to fix that would be
takesBTuple((a:B) -> a)
Otherwise, you can define a trait ConvertibleToB
that has ->
as a method, and then this would work.
val a = new A
val aa = new AA
//all of these work
takesBTuple(tupleMaker(a, a))
takesBTuple(a -> a)
takesBTuple(aa -> a)
takesBTuple(a -> aa)
takesConvertibleToBTuple(a -> a)
takesConvertibleToBTuple(a -> aa)
//Just for demonstration
def takesConvertibleToBTuple(t: (ConvertibleToB, ConvertibleToB)): Unit = println("took " + t)
trait ConvertibleToB {
def convertToB: B
def ->(b: B): (B, B) = (convertToB, b)
def ->(a: this.type): (this.type, this.type) = (this, a)
}
class A extends ConvertibleToB {
override def convertToB: B = new B
}
class AA extends ConvertibleToB {
override def convertToB: B = new B
}
implicit def makeB(c: ConvertibleToB): B = c.convertToB
The only implicit def you need to import is makeB
. I don't know if this is what you want or if it will be practical for you, though, especially if you don't want tuples of type (A, AA)
to be converted to (B, B)
Link to Scastie: https://scastie.scala-lang.org/pPuzw0sSQfKlglfT0a3Rrw
Upvotes: 1