Scala - 如何在使用之前排除函数的泛型类型?
Scala - How can I exclude my function's generic type until use?
我有一个 String
到 Function
的映射,它详细说明了一种语言中的所有有效函数。当我向地图添加功能时,我需要指定类型(在本例中 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 将该方法提升为函数,它需要捕获隐式,因此它必须知道您想要哪种类型。
我有一个 String
到 Function
的映射,它详细说明了一种语言中的所有有效函数。当我向地图添加功能时,我需要指定类型(在本例中 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 将该方法提升为函数,它需要捕获隐式,因此它必须知道您想要哪种类型。