在Scala中提取独立函数的方法

时间:2012-10-07 11:12:33

标签: scala oop functional-programming scala-macros

给出类似的东西:

class A {
  def f(x: X) = ...
  def g(y: Y, z: Z) = ...
  ...
}

如何(自动)提取功能:

object A {
  val f' = (a: A, x: X) => a.f(x)  // do this automagically for any arbitrary f
  val g' = (a: A, y: Y, z: Z) => a.g(y, z) // and deal with arity > 1 too
  ...
}

使用此完全类型签名(首先是对象,然后是参数列表)。让我非常清楚地说明问题:

  

“如果在类f(x1: X1, ..., xn: Xn)的上下文中定义了方法A,如何自动提取(i)的函数f'   接收a类型的实例A(ii)f的参数列表1:1对应的参数列表,即。 x1: X, ... xn: Xn,哪个实施正好a.f(x1: X1, ..., xn: Xn)

甚至:

  

捕获lambda演算的扩展性的概念,例如   您λx.(f x)时自动从f提取x   在f

中似乎没有空闲

首先可以通过找到访问标识符fg,...的方式来解决这个问题,而不需要特定的a: A(一个具有特定的A后来,当然)。我们可以简单地手写f'g',但让我们放纵干燥。


P.S。如果没有运行时反射,这可能是不可能的(虽然Scala 2.10+宏可能是这样),因为我似乎无法找到一种方法来引用fg的标识符事先的特定实例(a: A)。但它会像以下一样,而不必诉诸strings

A.getMethod("f"): Function2[A, X, ...]

我也意识到问题的实际应用可能有助于参与者提出替代方案,但我是在抽象意义上讨论这个问题。我不是要解决我已经减少到这个问题的其他问题。我想知道这个是否可能:-)这里有very nice article来真正理解这个问题背后的动机,对Scala进行Eta扩展的咆哮。

3 个答案:

答案 0 :(得分:2)

你当然可以在编译时用宏来做这件事 - 我在the preview release of ScalaMock 3做了类似的事情 - 模拟对象是匿名类的实例,其中每个成员都是由mock函数的实例实现的。你很可能将它作为你想要做的事情的起点。

警告:ScalaMock 3目前仅适用于Scala 2.10.0-M6。它不适用于M7或当前的开发版本,因为我没有(还有!)有机会解决宏API中的重大变化。

答案 1 :(得分:0)

这类似于为{1}}项目创建Lensed的Scala案例类。我相信可以修改它来创建你描述的方法而不是镜头。

答案 2 :(得分:-2)

我知道这不是一个特别漂亮的解决方案,但是如果你不能使用宏,你可以为你需要的伴随对象生成源代码,并在单独的构建步骤中编译它。

def generateCompanionObject(clazz: Class[_]) = {
     val className = clazz.getName.split("\\.").last
     val firstMethod = clazz.getMethods.head//for simplicity
     val methodName = firstMethod.getName
     val parametersClasses = firstMethod.getParameterTypes.map(_.getName).toSeq

     val objectDefinition = "object " + className + " {\n" +
         generateMethodDefinition(className, methodName, parametersClasses) +
         "\n}"

     objectDefinition
 }

 def generateMethodDefinition(className: String, methodName: String, parameterClasses: Seq[String]) = {
     val newMethodName: String = "   def invoke" + methodName.capitalize + "On"

     val parameterList: String = "(o:" + className + ", " + parameterClasses.zipWithIndex.map {
         case (argClassName, index) => "arg" + index + ": " + argClassName
     }.mkString(", ") + ")"

     val generateOldMethodCall: String = "o." + methodName + parameterClasses.zipWithIndex.map {
         case (argClassName, index) => "arg" + index
     }.mkString("(", ",", ")")

     newMethodName + parameterList + " = " + generateOldMethodCall
 }

为班级

class A {
   def foo(x: String, y: String) = x + y
}

它会生成

object A {
   def invokeFooOn(o:A, arg0: java.lang.String, arg1: java.lang.String) = o.foo(arg0,arg1)
}