Dodo doc > Variables and Functions > Linked References


Linked References

A link is an object that can be shared to establish a relationship between several objects. If a value is associated with the link it is called a linked reference.

Linked references are often used to store values that are shared by more than one object instance. A linked reference can also be used in a function, where an argument by reference cannot.

Requirements

To use linked references, the dodo file should contain:

use link
Rationale: Linked references are provided as a service, and no service namespace is included by default in dodo programs. So the namespace needs to be included manually.

Declaration

Linked references are variables or constants of type "Link" with a target type. If they don't have an initial value, their initial state is "null" and they cannot be converted to the target type. The Link constructor is used to create a linked reference with an initial value.

A linked reference can be either active or inactive. If the type has "*" in front then the linked reference is active else it is inactive. An inactive reference cannot become active.

An inactive reference can be read but cannot be updated.

Examples:

*Link(<with: House>) property  # initially uninitialised
def number = Link(10)
Rationale: Link is a capability type provided by the link service. Being a capability is what allows the variable to be used in a function when a reference can not. Apart from shared storage, another use of linked references is to allow side effects in functions.
By default, a linked reference state starts as "null" for times when it is desirable that the value is uninitialised first. This mimics the behaviour of references in other languages.

Use in expressions

Where necessary, dodo converts a linked reference to its target type so it can be used in the same way as the target type. A linked reference in "null" state cannot be converted to its target type and using it the same way raises an exception.

Examples:

draw(paper, property)  # Exception
2 * number
Rationale: It is clear why an exception is raised when trying to use an uninitialised value. Automatic conversion to the target type allows a lighter syntax than explicit conversion.

Use as argument by reference

Passing a variable by reference never involves a conversion. Because a Link variable is not of the same type as the target, linked references have a special syntax that allow the operation to use directly the target so no conversion is required.

That special syntax is to prefix the variable name with "link!". For assignment there is a shorthand notation using "*".

Example:

*property = showHome  # same as .link!property = showHome
Rationale: The alternative is to pass a reference to the Link variable itself, which prevents from using it when the target type would be expected and raises other concerns. In particular the linked reference would stop being shared when giving a new value by assignment.
The dodo syntax makes it clear which service the capability belongs to.

Storage and function parameters

Like other linked references, a stored reference can be active or inactive. Only an active reference can be copied to an active stored reference. Any linked reference can be copied to an inactive reference.

There can only be one active reference at a time. After it is stored the linked reference can only be accessed through the object that contains it.

Example:

new struct {
  Link(<with: House>) house = property  # inactive
}

A function declaration can include an active linked reference parameter. Its type is prefixed with "*" to mark it as active. If it is prefixed with "**" instead, there is the additional effect that the ownership of the linked reference moves to the function and the caller can no longer use it.

When calling the function the variable name needs to be prefixed with "*".

Example:

def addToDirectory(*Link(<with: Directory>), Link(<with: House>), Details)

addToDirectory(*directory, property, details)
Rationale: Although dodo functions are considered pure and free of side effects linked references allow to bypass these restrictions to a certain extent. An active linked reference allows side effects, that is why it is prefixed with a star when used.
Since an active reference can only be created in a method or constructor, the paths that lead to side effects in functions always originate from parts of code where side effects are allowed.
Transfer of ownership is a mechanism that avoids relying on reference counting or garbage collection for memory management.

Finding linked references

If there is a list of objects "directory" with a linked reference attribute house, the objects that link to a particular reference property can be found with:

directory[where house = property]
Rationale: This equality compares the links rather than the target objects which ensures the directory entries are linked to the selected reference.

Joining two lists by a linked reference

If the objects in a first list directory are related to the objects in a second list street by sharing a linked reference house, the two lists can be joined together with:

[for entry in directory; for address in street; where entry.house = address.house]
Rationale: The = operator returns true when the linked reference is shared by both objects, which denotes a relational link between them.

^ 3.2. Constants and Enums

v 3.4. Functions