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 Node
s 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]{...}
精彩评论