Scala:一个涉及匿名子类,回调和类型参数的棘手案例

时间:2011-08-02 19:45:45

标签: scala

我甚至不确定如何描述我在做什么,除了一个例子:

class Node

abstract class App {
    def schema: Node
}

def bind(app: App, f: Node => Node) {
    f(app.schema)
}

val app = new App {
    val schema = new Node {
        val child = new Node
    }
}

bind(app, _.child)

这不编译。我从error: value child is not a member of this.Node电话中获得bind

我不知道如何解决这个问题,但我认为它可能涉及使用参数化类型。我需要f的参数类型为分配给Node的实际schema子类的参数。

编辑:我无法明确命名我的Node子类型,因为在现实生活中我有完整的静态定义Node树,并且名称不实用它们。

3 个答案:

答案 0 :(得分:4)

Ǹode没有方法child所以类App必须保留封闭节点的参数:

abstract class App[N <: Node] {
  def schema: N
}

然后你可以扩展Node以包含child:

class ParentNode extends Node {
  def child = new ParentNode
}

最后,您可以将bind写为:

 def bind[N <: Node](app: App[N], f: N => N) = {
    f(app.schema)
 }

 val app = new App[ParentNode] {
    val schema = new ParentNode
 }

答案 1 :(得分:3)

您可以执行以下操作:

def bind[T](app: App{val schema: T}, f: T => Node) { f(app.schema) }
bind[{val child: Node}](app, _.child)

我认为这对你想要达到的目标来说仍然过于冗长。

答案 2 :(得分:2)

bind已签名App x (Node => Node) => Node

此上下文中的

_.child表示{n: Node => n.child},而Node未定义子项。这是您的错误消息告诉的内容。

在您的特定情况下,您可以期望它可以工作,因为您知道bind的函数参数应用于app参数的模式节点。但是,您应该告诉编译器几乎一样多。这将迫使您将一些实施细节更公开。

首先,也许你的函数不会有Node参数,但更精确。你可以拥有一个通用参数:

def bind[N <: Node](app: App, f: N => Node)

但是当你打电话给f(app.schema)时,你必须确保app.schema具有所需的类型

class App[N <: Node] {def schema: N}
def bind[N <: Node](app: App[N], f: N => Node) = f(app.schema)

最后,你必须使你的应用程序的类型更明确,至少

class NodeOfApp{def child: Node}
val app = new App[NodeOfApp]{...}