在不同 return 类型的情况下克服 Scala 中的类型擦除

Overcoming type erasure in Scala in cases of different return types

我想定义以下两个 Scala 函数:

def Pr(
  f: Int => Int,
  g: Tuple3[Int, Int, Int] => Int
): Tuple2[Int, Int] => Int = {
  def recurse(x: Int, counter: Int): Int = counter match {
    case 0 => f(x)
    case y => g(x, y-1, recurse(x, y-1))
  }
  (recurse _).tupled
}

def Pr(
  f: Tuple2[Int, Int] => Int,
  g: Tuple4[Int, Int, Int, Int] => Int
): Tuple3[Int, Int, Int] => Int = {
  def recurse(x1: Int, x2: Int, counter: Int): Int = counter match {
    case 0 => f(x1, x2)
    case y => g(x1, x2, y-1, recurse(x1, x2, y-1))
  }
  (recurse _).tupled
}

这里的基本思想是,如果f是n元,g是(n+2)元,那么Pr(f,g)是(n+1)元.当然,问题是类型擦除。类型擦除后 fg 都是 Function1 的,两个 Pr 函数是无法区分的。我认为创建一个带有类型标签的 Pr 函数可能会有所帮助,但我不确定如何在每种情况下更改 return 类型。

乐于接受任何建议;那些可以在不使用额外的库(Shapeless,Scalaz)的情况下解决这个问题的是首选。

对于另外 5%,标准习惯用法(早于磁化)是添加一个虚拟隐式参数列表:

def Pr(
  f: Tuple2[Int, Int] => Int,
  g: Tuple4[Int, Int, Int, Int] => Int
)(implicit dummy: DummyImplicit): Tuple3[Int, Int, Int] => Int = {
  def recurse(x1: Int, x2: Int, counter: Int): Int = counter match {
    case 0 => f(x1, x2)
    case y => g(x1, x2, y-1, recurse(x1, x2, y-1))
  }
  (recurse _).tupled
}

肯定有无数的问答。

Scala 重载只考虑第一个参数列表;虚拟参数用于消除已擦除签名的歧义。

模重载是邪恶的,Travis Brown 的博客是关于在 Scala 中做一些你可能不应该做的不可能的事情,他说这可能不是一件好事。全面披露,tl;博士