Scala - 如何在使用之前排除我的功能的通用类型?

时间:2015-07-14 00:03:49

标签: scala generics typeclass implicit

我有一张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)

2 个答案:

答案 0 :(得分:5)

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

另一种方法是在地图中为每个参数类型存储一个单独的函数对象。您可以使用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)

不需要解析任何类型类隐式调用callFn(),因为隐含的Numeric已在调用addFn时得到解决。

如果我们在调用函数时尝试解析类型类会怎么样?

第一个问题是Function1(或Function2)不能有隐式参数。只有一种方法可以。 (有关详细说明,请参阅this other question。)因此,如果您想要的内容类似于Function1但采用隐式参数,则您需要创建自己的类型来定义{{1} } 方法。但它必须是与apply()不同的类型。

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

Function1

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

如果您可以为带有数值的一元函数设置单独的映射,则(相对)容易:

Numeric

你可以在它需要的类型类上使函数trait泛型,让你的map包含使用不同类型类的一元函数。这需要在内部进行强制转换,并将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) 的规范移动到最终调用函数的位置:

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)

此外,这些解决方案都不支持需要类型类的功能。被强制要明确哪些函数采用隐式参数,以及它们使用什么类型的含义,开始首先打败使用含义的目的。

答案 1 :(得分:3)

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