Getting inherited Superclass methods to return an instance of the Subclass?

257 views
Skip to first unread message

Haoyi Li

unread,
Mar 4, 2012, 12:13:15 PM3/4/12
to scala-user
Hey All,

I'm trying to write a simple HTML-generating internal DSL, that uses method chaining to augment the nodes as I generate them. For example:

nodes = 
   div.cls("bordered").background_color("yellow")(
      p.line_height(13).color("blue")(
         hello world
      )
   )

would generate

<div class="bordered" style="background-color: yellow">
    <p style="line-height: 13px; color: blue">
         hello world
    </p>
</div>

The idea is that each method call will augment the item it was called on. Internally, this is pretty straightforward:

class AnyNode(...){
   def cls(v: String) = this.copy(classes = v :: this.classes)
}

Where each call does a copy-and-update, returning the newly created object.

My problem is that some methods only exist on sub-classes. For example, I want to be able to do

table.head(...).body(...)

while disallowing

div.head(...).body(...)

Hence "head()" is defined as a method of the Table class, and not of the AnyNode class. However, all the generic operations are defined on the AnyNode class, and thus return objects of type AnyNode. As a result, I can do this:

table.head(...).cls("bordered")

but I cannot do this

table.cls("bordered").head(...)

because cls returns an AnyNode. Is there any way to get cls (which Table inherited from AnyNode), when called on a instance of Table, to return another instance of Table? Or is it impossible, and should I be trying a different approach to this?

Thanks!
-Haoyi


Jan Vanek

unread,
Mar 4, 2012, 1:10:24 PM3/4/12
to Haoyi Li, scala...@googlegroups.com
Use this.type.

Example:

class Node {
def cls(c: String): this. type = { whatever; this }
}

Regards,
Jan

Roland Kuhn

unread,
Mar 4, 2012, 1:21:58 PM3/4/12
to Jan Vanek, Haoyi Li, scala...@googlegroups.com

On Mar 4, 2012, at 19:10 , Jan Vanek wrote:

> Use this.type.
>
> Example:
>
> class Node {
> def cls(c: String): this. type = { whatever; this }
> }
>

this.type means “exactly this object”, which does not fit the OP’s requirements (return updated copy). Unfortunately, the issue is not a simple one, search this group for MyType. In essence you would need to go the same route as the collections library (using the CanBuildFrom pattern).

Regards,

Roland

Roland Kuhn
Typesafe – The software stack for applications that scale.
twitter: @rolandkuhn


Tomas Mikula

unread,
Mar 5, 2012, 1:57:59 PM3/5/12
to scala-user
You can add a type parameter to AnyNode that represents the actual
subclass:

abstract class AnyNode[Self](...){
def copy(...): Self
def cls(v: String): Self = ...
}

class Table extends AnyNode[Table](...) {
...
}

Regards,
Tomas
Reply all
Reply to author
Forward
0 new messages