给出类似的东西:
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
。
首先可以通过找到访问标识符f
,g
,...的方式来解决这个问题,而不需要特定的a: A
(一个具有特定的A
后来,当然)。我们可以简单地手写f'
或g'
,但让我们放纵干燥。
P.S。如果没有运行时反射,这可能是不可能的(虽然Scala 2.10+宏可能是这样),因为我似乎无法找到一种方法来引用f
或g
的标识符事先的特定实例(a: A
)。但它会像以下一样,而不必诉诸strings
:
A.getMethod("f"): Function2[A, X, ...]
我也意识到问题的实际应用可能有助于参与者提出替代方案,但我是在抽象意义上讨论这个问题。我不是要解决我已经减少到这个问题的其他问题。我想知道这个是否可能:-)这里有very nice article来真正理解这个问题背后的动机,对Scala进行Eta扩展的咆哮。
答案 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)
}