6

给定类似的东西:

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,如何自动提取一个函数f'该函数(i) 接收类型的实例,以及aA(ii)与 1:1 的参数列表对应的参数列表,fx1: X, ... xn: Xn实施正是a.f(x1: X1, ..., xn: Xn)

甚至:

捕获 lambda 演算的可扩展性概念 这样就可以自动λx.(f x)从.fxf

这可以首先通过找到一种访问标识符的方法来解决fg... 我们可以简单地书写或手写,但让我们沉迷于干燥。a: AAf'g'


PS 也许如果没有运行时反射,这是不可能的(尽管使用 Scala 2.10+ 宏可能是可能的),因为我似乎无法找到一种方法来事先引用fg不引用特定实例 ( a: A) 的标识符。但这将类似于以下内容,而不必求助于strings

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

我也意识到这个问题的实际用途可能会帮助参与者提出替代方案,但我是在抽象意义上讨论这个问题。我不是试图解决我已经简化为这个问题的其他问题。我想知道是否可能:-) 这是一篇非常好的文章,可以真正理解这个问题背后的动机,并对 Scala 上的 Eta 扩展进行了咆哮。

4

3 回答 3

2

您当然可以在编译时使用宏来执行此操作——我在ScalaMock 3 的预览版中做了非常类似的事情——模拟对象是匿名类的实例,其中每个成员都由模拟函数的实例实现。您很可能可以将其用作您尝试做的事情的起点。

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

于 2012-10-07T14:37:41.617 回答
0

这感觉类似于为 Scala 案例类创建镜头的Lensed项目。我相信可以对其进行修改以创建您描述的方法而不是镜头。

于 2012-10-07T20:27:33.993 回答
-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)
}
于 2012-11-04T14:20:25.323 回答