How can an object hold a reference to itself, like a doubly linked node that starts as a ring of one?
← Classes and OOP · Ref: Q1471
A property of the object's own type may refer back to the object: assign 'this' to it in the constructor, with ':', ':=', '=' or ':=?' ('prev: this', 'this.owner :=? this'). Storing the object in one of its OWN properties is not a use of it - nothing is read through it and nothing outside the object holds it - so it sets that property like any other assignment, even while other properties are still unset. A doubly linked node can start as a ring of one before its label is set:
Node
prev as Node?
next as Node?
label as String?
default private Node()
Node()
-> text as String
prev: this
next: this
label: text
label()
<- rtn as String: label
override operator ? as pure <- rtn as Boolean: label?
THE PROPERTY IS THE OBJECT UNDER ANOTHER NAME
Only 'this' itself may be stored that way, and in that constructor the property then IS the object. Calling a method through it ('itself.doubled()'), copying it to a local, or reading 'this.itself' is a use of the object, so it needs every property set first, exactly as a method call on 'this' does (E08061; see Q1396). Passing it to another object's method is publishing a half-built object (E08250), as passing 'this' is. Once every property is set it can be used freely. The same store one step removed - a child created with a capture of 'this' and kept in the object's own property ('section :=? (owner: this) with trait of Part as class ...') - is the parent link: see Q2181.
GENERATED OPERATORS ARE SAFE ON A CYCLE
A type like this can reach itself through its properties, and the compiler works that out. Its GENERATED operators ('default operator ?', '$', '$$', '==', '#?') then carry a cycle guard, so a property that refers back never recurses without end: '?' treats the object it is already checking as set; '$' and '$$' on a cycle are unset (never JSON with a hole where the loop was); '==' assumes a pair it is already comparing is equal, so two identical rings are equal; and '#?' leaves out the properties that lead back. A type that cannot reach itself pays nothing. The node above still writes its own '?', because only its label says whether it is set.
Example
#!ek9 defines module qa.oop.property.refers.to.itself defines class Node prev as Node? next as Node? label as String? default private Node() Node() -> text as String prev: this next: this label: text label() <- rtn as String: label next() <- rtn as Node: next prev() <- rtn as Node: prev linkAfter() -> node as Node follower <- next node.joinBetween(this, follower) follower.joinPrev(node) next: node joinBetween() -> before as Node after as Node prev: before next: after joinPrev() -> node as Node prev: node override operator ? as pure <- rtn as Boolean: label? Tally itself as Tally? count as Integer? total as Integer: 0 default private Tally() Tally() -> start as Integer itself: this count: start total: itself.doubled() doubled() <- rtn as Integer: count * 2 value() <- rtn as Integer: total override operator ? as pure <- rtn as Boolean: total? defines program SelfReferenceDemo() stdout <- Stdout() first <- Node("first") stdout.println(`after first: ${first.next().label()}`) stdout.println(`before first: ${first.prev().label()}`) first.linkAfter(Node("second")) second <- first.next() stdout.println(`ring: ${first.label()}, ${second.label()}, ${second.next().label()}`) stdout.println(`back: ${first.prev().label()}`) stdout.println($Tally(21).value())
Common mistakes
E08061 — 'itself' was set to 'this', so calling doubled() through it is a call on the object - and doubled() reads 'count', which is not set yet. Storing 'this' is fine at any point; reading through it waits until every property is set.
Incorrect:
total: itself.doubled() count: start
Correct:
count: start total: itself.doubled()
Other ways to ask this
- Can a constructor assign 'this' to one of the object's own properties?
- How do I write 'me: this' in an EK9 constructor without E08061?
- How do I build a circular linked list node whose next and previous point to itself?
- Why is reading through a property set to 'this' in the constructor still E08061?
Coming from another language?
Java: 'this.next = this;' in a constructor compiles, and so does every use of a half-built object - the compiler checks neither. Swift: two-phase initialisation - 'self' cannot be used until every stored property is set, so a property that must point at 'self' is declared optional or implicitly unwrapped and set after. Kotlin: 'lateinit var next: Node' and 'next = this' in init, with a 'Leaking this' warning on any other use. EK9: storing 'this' in an own property is allowed at any point in the constructor; any read through that property waits for the object to be complete (E08061).
Keywords: E08061, E08250, circular, constructor, doubly linked, linked list, node, property, ring, self reference, self-referential