简体   繁体   中英

Scala trait `this.type` in type parameter

Have a look at these two simple traits:

trait TreeNode1[S] {
    def subNodes: List[S]
}
trait TreeNode2 {
    def subNodes: List[this.type]
}

(Not best naming, renamed them just to be brief.)
TreeNode1 defines a tree node with its children access, pointing their type S .
TreeNode2 defines the same, but its children have the same type as the class the current trait is mixed in (another words, tree node with uniform subnodes).

In theory TreeNode2 is a particular case of TreeNode1 :

trait TreeNode2 extends TreeNode1[this.type] {...}

But Scala will not compile TreeNode2 with such an extension, because this.type can not be used in such way, although there are no any inconsistencies with its working in runtime.

How can I get around this situation? Or Scala do not offer such poorly used mechanism?


The reason I need this construction is the following:

I have another trait that requires TreeNode1 to be mixed in. I also have some class that mixes TreeNode1 with quite another children type. But I also have several classes which have the same type as they are:

class SomeTreeNode extends TreeNode1[SomeTreeNode]

So it will look prettier if I use TreeNode2 for it:

class SomeTreeNode extends TreeNode2

Implementing the same logic. But for using TreeNode2 should be a case of TreeNode1 , which it actually is, but Scala doesn't agree with me.

PS At least it is wondering as theoretical question about Scala, not for a wide practical use.

its children have the same type as the class the current trait is mixed

No. This is a common misunderstanding. this.type is the singleton type of this ; ie the type whose only two values are this and null . All children of a TreeNode2 instance must be the same instance.

To answer the other part of your question, one option is to make S a type member instead of a type parameter:

trait TreeNode1 {
    type S
    def subNodes: List[S]
}
object TreeNode1 {
    // use TreeNode1.Aux[S] where you had TreeNode1[S] originally
    type Aux[T] = TreeNode1 { type S = T }
}

trait TreeNode2 {
    type S = this.type // not really what you want
    def subNodes: List[this.type]
}

(so-called Aux pattern), but whether this works for you depends on how they are used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM