自 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(_))
但是,通过这种方式,您必须同时传递 In
和 Out
作为类型参数。如果单个 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/