如何在 Scala 中创建一个支持隐式转换的内联函数?

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

以如下代码为例:

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")
}

如何使用内联函数获得 TupleMaker 的行为(特别是 w.r.t。隐式转换)?我可以同时修改 class A 和 B,并使用任何中缀运算符(不必是现有运算符)如果这有助于解决问题。

您可以再添加一次转化

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

更通用的解决方案是

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 

更简单

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

不起作用,因为首先推断出 X(成为 A)。所以ev: T <:< (X1, Y1)是一个技巧,让X1Y1先推断出来(成为B)。隐式从左到右解析。

解决这个问题的简单方法是

takesBTuple((a:B) -> a)

否则,您可以定义一个具有 -> 作为方法的特征 ConvertibleToB,这样就可以了。

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

您需要导入的唯一隐式定义是 makeB。不过,我不知道这是否是您想要的,或者它是否对您实用,特别是如果您不希望将 (A, AA) 类型的元组转换为 (B, B)

Link 给斯卡斯蒂:https://scastie.scala-lang.org/pPuzw0sSQfKlglfT0a3Rrw