I've searched a bit around but I was not able to find an example of such a structure:
Person[P <: Person[P]]
that is explained in a way I understand.
How does this resolve? It kinda looks like an endless recursion to me, but it seems I am wrong with that conclusion.
Self Referential structures are those structures that have one or more pointers which point to the same type of structure, as their member.
A self type refers to the type on which a method is called (more formally called the receiver). If a self type is used in an inherited method, it represents a different type in each class that declares or inherits that method–namely that specific class, no matter whether it declared or inherited the method.
Generics are an important step towards PHP maturing as a gradually-typed language - a direction in which PHP is already moving, with the addition of scalar type-hints, and return type-hints, in PHP 7.
The structure itself is explained in the Twitter Scala School and is called F-bounded polymorphism.
// you define it like this
trait X extends Person[X]
// it then gets expanded to this
trait Person[X extends Person[X]]
This structure is used when the trait needs to have a reference to the type of the object it is extending. If the Scala school explanation is not enough you can search the internet for 'F-bounded polymorphism'
This is sometimes called a self type (not to be confused with Scala's explicitly typed self-references) and it is usually used in order to have method signatures that are strong enough to express that the method works with objects of the same type as the receiver.
Let's look at an example. Say, you have a base trait Animal
with a generic breed
method that takes another Animal
and returns an Animal
.
trait Animal {
def breed(a: Animal): Animal
}
Ok, but what you actually want is a breed
method that expresses that every concrete animal only breeds with animals of the same class and also returns an animal of the same class. The following implementation
class Cow extends Animal {
def breed(c: Cow) = new Cow
}
is not possible, because this breed
's signature doesn't match. Overriding is also not possible, because you would need to change the argument type covariantly, which is forbidden.
Self-types to the rescue:
trait Animal[A <: Animal[A]] {
def breed(a: A): A
}
class Cow extends Animal[Cow] {
def breed(c: Cow) = new Cow
}
As EECOLOR already pointed out, the type theory behind it is called F-bounded polymorphism.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With