Scala - 如何在使用之前排除函数的泛型类型?

Scala - How can I exclude my function's generic type until use?

我有一个 StringFunction 的映射,它详细说明了一种语言中的所有有效函数。当我向地图添加功能时,我需要指定类型(在本例中 Int)。

var functionMap: Map[String, (Nothing) => Any] = Map[String, (Nothing) => Any]()

functionMap += ("Neg" -> expr_neg[Int])

def expr_neg[T: Numeric](value: T)(implicit n: Numeric[T]): T = {
  n.negate(value)
} 

相反,我该如何做:

functionMap += ("Neg" -> expr_neg)

没有 [Int] 并在稍后调用时添加它:

(unaryFunctionMap.get("abs").get)[Int](-45)

您正在尝试使用 classes 类型(在本例中为 Numeric)构建您的函数。 Type classes 依赖于隐式参数。隐含在编译时解决。您的函数名称字符串值仅在 运行 时已知,因此 您不应像这样在类型 class 之上构建解决方案。

另一种方法是在地图中为每个参数类型存储一个单独的函数对象。您可以使用 TypeTag:

存储参数类型
import scala.reflect.runtime.universe._

var functionMap: Map[(String, TypeTag[_]), (Nothing) => Any] = Map()

def addFn[T: TypeTag](name: String, f: T => Any) =
  functionMap += ((name, typeTag[T]) -> f)

def callFn[T: TypeTag](name: String, value: T): Any =
  functionMap((name, typeTag[T])).asInstanceOf[T => Any](value)

addFn[Int]("Neg", expr_neg)
addFn[Long]("Neg", expr_neg)
addFn[Double]("Neg", expr_neg)

val neg10 = callFn("Neg", 10)

无需解析类型 class 隐式调用 callFn(),因为隐式 Numeric 已在调用 addFn.[=31= 时解析]


如果我们在调用函数时尝试解析类型 class 会发生什么情况?

第一个问题是Function1(或Function2)不能有隐式参数。只有一个方法可以。 (有关更多解释,请参阅 this other question。)因此,如果您想要一些类似于 Function1 但采用隐式参数的东西,您需要创建自己的类型来定义 apply() 方法.不过,它必须是与 Function1 不同的类型。

现在我们来解决主要问题:所有隐式必须能够在编译时解析。在代码中方法是 运行 的位置,选择隐式值所需的所有类型信息都需要可用。在以下代码示例中:

unaryFunctionMap("abs")(-45)

我们真的不需要指定我们的值类型是 Int,因为它可以从值 -45 本身推断出来。但是我们的方法使用 Numeric 隐式值这一事实无法从该行代码中的任何内容中推断出来。我们需要在编译时的某处指定使用Numeric

如果您可以为采用数值的一元函数单独映射,这(相对)容易:

trait UnaryNumericFn {
  def apply[T](value: T)(implicit n: Numeric[T]): Any
}

var unaryNumericFnMap: Map[String, UnaryNumericFn] = Map()

object expr_neg extends UnaryNumericFn {
  override def apply[T](value: T)(implicit n: Numeric[T]): T = n.negate(value)
}

unaryNumericFnMap += ("Neg" -> expr_neg)

val neg3 = unaryNumericFnMap("Neg")(3)

您可以使函数特征在它需要的类型 class 上通用,让您的映射包含使用不同类型 class 的一元函数。这需要内部转换,并将 Numeric 的规范移动到最终调用函数的位置:

trait UnaryFn[-E[X]] {
  def apply[T](value: T)(implicit ev: E[T]): Any
}

object expr_neg extends UnaryFn[Numeric] {
  override def apply[T](value: T)(implicit n: Numeric[T]): T = n.negate(value)
}

var privateMap: Map[String, UnaryFn[Nothing]] = Map()

def putUnary[E[X]](key: String, value: UnaryFn[E]): Unit =
  privateMap += (key -> value)

def getUnary[E[X]](key: String): UnaryFn[E] =
  privateMap(key).asInstanceOf[UnaryFn[E]]

putUnary("Neg", expr_neg)

val pos5 = getUnary[Numeric]("Neg")(-5)

但是你还是要在某处指定Numeric

此外,这些解决方案都不支持 不需要 class 类型的函数。被迫如此明确地说明哪些函数采用隐式参数,以及它们使用什么样的隐式,这首先违背了使用隐式的目的。

你不能。因为 expr_neg 是一种带有类型参数 T 和隐式参数 n 的方法,具体取决于该参数。对于 Scala 将该方法提升为函数,它需要捕获隐式,因此它必须知道您想要哪种类型。