scala - 在 Scala 中,如何在不知道输出类型或完整类型参数的情况下调用适用于输入类型的多态函数?

标签 scala scala-2.13 polymorphic-functions

自 Scala 2.12(或者是 2.13,不能确定)以来,编译器可以跨多个方法推断潜在类型参数:

    def commutative[
        A,
        B
    ]: ((A, B) => (B, A)) = {???} // implementing omitted

val a = (1 -> "a")
val b = commutative.apply(a)

最后一行成功推断出A = Int, B = String,不幸的是,这需要给定一个实例a: (Int, String)

现在我想稍微改变一下这个 API 并定义以下函数:

def findApplicable[T](fn: Any => Any)

findApplicable[(Int, String)](commutative) 自动生成专门用于 A = Int, B = String 的正确函数。有没有办法在语言的能力范围内做到这一点?或者我必须升级到 scala 3 才能执行此操作?

UPDATE 1 需要注意的是,commutative 的输出可以是任何类型,不一定是 Function2,例如我试过以下定义:

trait SummonedFn[-I, +O] extends (I => O) {

    final def summon[II <: I]: this.type = this
}

然后重新定义commutative来使用它:

    def commutative[
        A,
        B
    ]: SummonedFn[(A, B), (B, A)] = {???} // implementing omitted

val b = commutative.summon[(Int, String)]

糟糕,这行不通,类型参数不像值参数那样得到平等对待

最佳答案

如果在某些时候某些调用站点知道参数的类型(它们实际上不是 Any => Any),那么使用类型类是可行的:

trait Commutative[In, Out] {
  def swap(in: In): Out
}

object Commutative {

  def swap[In, Out](in: In)(implicit c: Commutative[In, Out]): Out =
    c.swap(in)

  implicit def tuple2[A, B]: Commutative[(A, B), (B, A)] =
    in => in.swap
}

在调用站点:

def use[In, Out](ins: List[In])(implicit c: Commutative[In, Out]): List[Out] =
  ins.map(Commutative.swap(_))

但是,通过这种方式,您必须同时传递 InOut 作为类型参数。如果单个 In 类型有多个可能的 Out,那么您无能为力。

但是如果你想要Input type => Output type implication,你可以使用依赖类型:

trait Commutative[In] {
  type Out
  def swap(in: In): Out
}

object Commutative {

  // help us transform dependent types back into generics
  type Aux[In, Out0] = Commutative[In] { type Out = Out0 }

  def swap[In](in: In)(implicit c: Commutative[In]): c.Out =
    c.swap(in)

  implicit def tuple2[A, B]: Commutative.Aux[(A, B), (B, A)] =
    in => in.swap
}

调用站点:

// This code is similar to the original code, but when the compiler
// will be looking for In it will automatically figure Out.
def use[In, Out](ins: List[In])(implicit c: Commutative.Aux[In, Out]): List[Out] =
  ins.map(Commutative.swap(_))

// Alternatively, without Aux pattern:
def use2[In](ins: List[In])(implicit c: Commutative[In]): List[c.Out] =
  ins.map(Commutative.swap(_))

def printMapped(list: List[(Int, String)]): Unit =
  println(list)

// The call site that knows the input and provides implicit
// will also know the exact Out type. 
printMapped(use(List("a" -> 1, "b" -> 2)))
printMapped(use2(List("a" -> 1, "b" -> 2)))

当您知道确切的输入类型时,这就是解决问题的方法。如果您不知道...那么您不能使用编译器(无论是在 Scala 2 还是在 Scala 3 中)来生成此行为,因为您必须使用一些运行时反射来实现此功能,例如使用 isInstanceOf 检查类型,转换为某些假设的类型,然后运行预定义的行为等。

关于scala - 在 Scala 中,如何在不知道输出类型或完整类型参数的情况下调用适用于输入类型的多态函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69092350/

相关文章:

ajax - SCALA Lift - Comet Actor 中的 S.param 访问

scala - 使用 Scala 在功能上生成游戏 Action

scala - 对于 Scala 2.13,更新具有数百万次更新的 LongMap、HashMap 或 TrieMap 的最快方法是什么?

scala - 使用多态函数映射泛型元组

c++ - 多态仿函数上的 std::result_of

scala 重写 None

scala - 从 ensime 运行 sbt 时出现 java.io.IOException?

scala - 将 scala 2.13 中的自定义集合操作添加到特定类型的任意集合

scala - 为未使用的变量使用占位符时出现 MatchError