简体   繁体   English

如何在 scala 中创建尊重隐式转换的内联 function?

[英]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?如何获得 TupleMaker 的行为(特别是 w.r.t. 隐式转换),但使用内联 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.如果对解决方案有帮助,我可以同时修改 class A 和 B,并使用任何中缀运算符(不必是现有的)。

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 ).不起作用,因为首先推断出X (为A )。 So ev: T <:< (X1, Y1) is a trick to make X1 , Y1 be inferred first (to be B ).所以ev: T <:< (X1, Y1)是首先推断出X1 , Y1的技巧(成为B )。 Implicits are resolved from the left to the right.隐式从左到右解析。

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.否则,您可以定义一个具有->作为方法的特征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

The only implicit def you need to import is makeB .您需要导入的唯一隐式 def 是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)不过,我不知道这是否是您想要的,或者对您是否实用,尤其是如果您不希望将(A, AA)类型的元组转换为(B, B)

Link to Scastie: https://scastie.scala-lang.org/pPuzw0sSQfKlglfT0a3Rrw链接到 Scastie: https://scastie.scala-lang.org/pPuzw0sSQfKlglfT0a3Rrw

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM