开发者

Scala: a tricky case involving anonymous subclasses, callbacks, and type parameters

I'm not even sure how to describe what I'm doing, except with an example:

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)

This does not compile. I get: error: value child is not a member of this.Node from the bind call.

I'm not sure how to fix this, but I think it probably involves use of parameterized types. I need the type of f's parameter to be that of the actual Node subclass assigned to schema.

EDIT: I can not explicitly name my Node subtypes, as in real life I have entire trees of statically defined Nodes and it would not be pr开发者_Go百科actical to name them.


Ǹode has no method child so class App must retain the parameter of the enclosed node:

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

Then you can extend Node to include child:

class ParentNode extends Node {
  def child = new ParentNode
}

Finally you can write bind as:

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

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


You could do something like:

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

I imagine that is still too verbose for what you are trying to achieve.


bind has signature App x (Node => Node) => Node

_.child in this context means {n: Node => n.child} and Node does not define child. Which is what your error message tells.

In your particular case, you could expect it to work, because you know the function argument of bind is applied to the schema Node of the app argument. However, you should tell nearly as much to the compiler. And that will force you to make some of the implementation details more public.

First, maybe your function will not have a Node argument, but something more precise. You could have that has a generic parameter :

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

But then when you call f(app.schema) then you must make sure that app.schema has the required type

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

Last you will have to make the type of your app val more explicit, at least

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜