Scala-泛型类型函数的映射

Scala-泛型类型函数的映射,scala,Scala,如何创建字符串到具有泛型类型的函数的映射?例如,我希望能够在编译时创建映射: var unaryFunctionMap: Map[String, Any => Any] = Map[String, Any => Any]() 然后,我想添加一些函数,这些函数可以接受String、Int、Double等类型的组合。可能的方式如下: unaryFunctionMap += ("Abs" -> Functions.add) unaryFunctionMap += ("Neg" -&

如何创建字符串到具有泛型类型的函数的映射?例如,我希望能够在编译时创建映射:

var unaryFunctionMap: Map[String, Any => Any] = Map[String, Any => Any]()
然后,我想添加一些函数,这些函数可以接受
String
Int
Double
等类型的组合。可能的方式如下:

unaryFunctionMap += ("Abs" -> Functions.add)
unaryFunctionMap += ("Neg" -> Functions.neg)
unaryFunctionMap += ("Rev" -> Functions.rev)
其中,我在我的
函数
类中实现了实际函数:

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

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

def rev(string: String): String = {
  string.reverse
}
因此,
abs
neg
都允许
Numeric
类型,而
rev
只允许
字符串
s。当我使用映射时,我希望能够在必要时指定键入。大概是这样的:

(unaryFunctionMap.get("abs").get)[Int](-45)
或者,如果这不可能

(unaryFunctionMap.get("abs").get)(Int, -45)

如何修改代码以实现此功能?

听起来您需要的是类型类。具体来说,对于数字,您实际上不需要定制的TypeClass,因为Scala已经提供了它们。如果您不想在所有类型之间共享行为,您只需扩展

object Test {
    // In this case you can probably even specialize to avoid unboxing.
    implicit class NumericOps[T : Numeric](val obj: T) extends AnyVal {
      def whatever(x: T): T = obj.add(x) // for instance
    }

}
然后,对于所有数字,您可以免费获得增强:

import Test._
5.whatever(6)// = 11

只要对字符串重复相同的模式,这是一个正常的pimp-my-library模式。如果您想在一堆不相关的类型上实现相同的操作,请查看类型类。

将函数存储为
Any=>Any
的问题是,函数的参数类型是逆变的,但返回类型是协变的,因此
Int=>Int
不是
Any=>Any
的子类型。您可以使用存在类型来解决这个问题,但这仍然不能提供类型安全性

作为一种解决方案,您可能希望使用
Map[Type,Map[String,=>\u]]]
并确保放入Map的每个条目都只使用相应类型的函数

以下是一个草图,而不是一个确定的解决方案;我对类型标记的了解不足以保证性能的正确性或合理性(这需要反射才能工作)

由于方法
get
中的显式强制转换,这可能是不安全的,因此我们需要确保
internal
的填充正确

用法示例:

object UnaryFunctionMap {
  def main(args: Array[String]) {
    val neg: Int => Int = i => -i
    val rev: String => String = s => s.reverse

    val map = new UnaryFunctionMap
    map += ("neg", neg)
    map += ("rev", rev)

    println(map.get[Int]("neg").map(_.apply(-45)))            // Some(45)
    println(map.get[String]("neg").map(_.apply("reverto")))   // None
    println(map.get[Int]("rev").map(_.apply(-45)))            // None
    println(map.get[String]("rev").map(_.apply("reverto")))   // Some(otrever)
  }
}

注意,对于任意类型的
A
,我假设函数为
A=>A
。如果您希望任意
A
B
的函数为
A=>B
,则需要将第二种类型添加到映射中,并相应地更新这两种方法

你想用这个干什么?这种类型的
Any=>Any
映射通常是Scala中要避免的反模式。了解您的用例可能有助于回答者确定一个更好的设计模式,以便在这里使用。我想创建一个函数名到函数的映射。这些函数可以是
String=>String
Int=>String
Double=>Double
,等等,所以我使用了
Any=>
。这些函数通常也是
T:Numeric=>T
,因此我希望能够在运行时传递
T
是什么类型的。这一点很清楚。我试图更好地理解您想要使用此映射的目的。我使用解析器将用户表达式解析为函数和操作数。地图包含了语言中的所有有效函数。我不确定这是否是一个更好的问题,但这更具体地说是我目前试图实现的:我不确定我是否理解你的建议。我如何在我的函数映射中实现这一点?我是否需要这样做:``functionMap+=(“Neg”->(value=>value.abs())
?当我尝试此操作时,会出现编译错误,因为
abs``不是
Any
的成员。
object UnaryFunctionMap {
  def main(args: Array[String]) {
    val neg: Int => Int = i => -i
    val rev: String => String = s => s.reverse

    val map = new UnaryFunctionMap
    map += ("neg", neg)
    map += ("rev", rev)

    println(map.get[Int]("neg").map(_.apply(-45)))            // Some(45)
    println(map.get[String]("neg").map(_.apply("reverto")))   // None
    println(map.get[Int]("rev").map(_.apply(-45)))            // None
    println(map.get[String]("rev").map(_.apply("reverto")))   // Some(otrever)
  }
}