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

I'm not even sure how to describe what I am doing, with the exception of 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 a bind call.

I'm not sure how to fix this, but I think this is probably due to the use of parameterized types. I need a type f that matches the actual Node subclass assigned by schema .

EDIT: I cannot explicitly specify subtypes of Node , since in real life I have whole trees of a statically defined Node , and it would be impractical to call them.

+4
source share
3 answers

ΗΈode does not have a child method, so the App class must save the attached node parameter:

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

You can then 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 } 
+4
source

You can do something like:

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

I believe that there is still too much for what you are trying to achieve.

+3
source

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

_.child in this context means {n: Node => n.child} , and Node does not define a child element. This is an error message.

In your particular case, you can expect it to work because you know that the argument to the bind function is applied to the Node scheme of the application argument. However, you should say almost as much to the compiler. And that will make you make some implementation details more public.

First, perhaps your function will not have a Node argument, but something more precise. You may have a general parameter:

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

But then when you call f(app.schema) , you have to make sure that app.schema is of the required type

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

Finally, you will need to make your application type more explicit, at least

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

All Articles