Links

A link is an object, which value is reference to another object. An object the link is linked to is called a link target.

The simplest way to create a link is by using a link unary operator:

'`' <target>

The more advanced syntax utilizes the type arguments:

<interface> '`' 'link' '[=' <target> ']'

or, when declaring a link field:

<field> [':'] {'='|'=>'|'=<'|'=<>'} <interface> '`' 'link' '=' <target>

where:

A link target is not known until the link is evaluated, which might only happen at run time. So, in general case, the link target is not known to the compiler. But compiler knows the basic type of the target, i.e. an object, the link target is derived from. This type is called a link interface.

The link interface can be specified explicitly:

Foo := integer` link = bar

In this case the link target should be compatible with its interface (i.e. the bar should be derived from integer).

Also, the link interface can be determined automatically:

  • When the link target expression is a reference to an object, the link interface is a target object itself:
    Target := "Target value"
    Foo := `target               ~~ Link interface is `target`.
    Bar := target` link = target ~~ The same as above.
    
  • Otherwise, the link interface is an ancestor of the target expression result:
    Link 1 := `"Target value" ~~ Link interface is `string`.
    Link 2 := `foo (          ~~ Link interface is `foo`.
    Bar := 2
    )
    Link 3 := `foo & bar      ~~ Link interface is `foo`.
    

Because the link target is only known at run time, the following limitations apply it:

  • the link target can not be used as sample;
  • when the link target is used as ancestor, the restrictions applied to the constructed object:
    • the constructed object can not have any samples;
    • the constructed object’s type parameters can not be altered;
    • the nested fields` ancestors can not be upgraded;
    • all of these restrictions apply to the nested fields.

The link target is evaluated on each access. An eager evaluation can be used to evaluate it only once.

As any other field, a link one is propagated to the derived object.

The link’s interface and target are expressions, so their evaluation in the scope of the derived object may result to a another objects. So, in the following code.

A := void (
  Foo := 1
  Bar := `foo
)
B := a (
  Foo = * (
    Baz := "baz"
  )
)

the b: bar link target and interface are b: foo, so the b: bar: baz reference is valid and results to a "baz" string.

A link override provides another target expression to the link. Another link interface expression can also be provided (but see the link target usage limitations).

A new link target should be compatible with (i.e. derived from) both new and old link interfaces.

A new link interface should be compatible with an old one, i.e. it should be derived from it.

Target := integer (
  Foo := 1
  = 1
)
A := void (
  Link := `target
)
B := a (
  Link = integer` * = target
  ~~ **Error**: `integer` is not derived from `target`.
)
C := a (
  Foo := target
  Link = target` * = foo
  ~~ Interface remains the same, while the target is changed.
)
D := a (
  Foo := target
  Link = `foo ~~ Both interface and target are changed.
)

If the backquote omitted in the link overrider, and a new link interface not specified explicitly, the link interface remains unchanged:

A := void (
  Link := `"target" ~~ Link interface is `string`.
)
Target := string (
  Foo := "foo"
  = "value"
)
B := a (
  Link = target ~~ The link target changed  to `target` object,
                ~~ while the interface remains the same: `string`.
)

Every link is an object inherited from the Link prototype, and is treated specially by o42a compiler. It allows the transparent access to the link target by dereferencing the link when necessary:

  • When a link member is referenced, the member is first searched for in the link object itself, and if not found, then it is searched for in the link target.
  • When converting a link to another type, an attempt to convert the link object itself is performed (e.g. by searching for appropriate adapter), and if failed - the link target is converted.
  • When interpreting the phrase with prefix resolving to a link, the clause corresponding to the first phrase part is first searched for in the link object itself, and if not found - in the link target.

A link can be created with an object constructor expression. The link interface can be specified as type argument:

Lnk := integer` link (
  ~~~
  Declare the link object.
  Link interface is `integer`.
  ~~~

  = 2 ~~ The plain value `2` is converted to the link automatically.
)

Sum := 40 + lnk ~~ Equals to `42`, as `lnk` is automatically dereferenced
                ~~ to the integer value `2`.

The Link object declares a phrase syntax for link creation:

String` link [= "string"]
String` link (            ~~ Canonical form
  = "string"
)

As mentioned above, a link is automatically dereferenced when required. But this can be done explicitly, with a link dereference expression:

<link> '->'

Usage examples:

~~ Array of links
Link array := string` link` array [[
  string` link [= "a"]
  string` link [= "b"]
  string` link [= "c"]
]]
Link array [1]     ~~ Refers the second element.
Link array [1]->   ~~ Refers the second element's link.
Link array [1]->-> ~~ Refers the target string of the second element's link
                   ~~ (`"b"`).

Any adapter can be declared as link. In contrast to plain adapter object, the adapter link object does not derive the adapter’s identifier. Instead, it is required that the link interface to be derived from the adapter’s interface. The link target will be used as an adapter instead of a link object itself.

Int :=> void (
  Value :=< `integer
  @String := `value @@string ~~ String representation of `int`.
)

Forty two := string (= Int (Value = 42))  ~~ String representation of `42`.
Forty two := string (
  = Int (Value = 42) @@string             ~~ Same as above.
)
Forty two := string (
  = (Int (Value = 42) @@string)->         ~~ Fully explicit version.
)

Links to other links are supported:

Integer link := `1
Link to integer link := `integer link
Link to link to integer link := integer` link` link` link = Link to integer link

This is discouraging however. It is highly recommended to avoid the links to another links. To reduce the discourage of the deep links usage the numerous limitations applied.

The second-level link is never dereferenced automatically. It should be dereferenced explicitly. Given the definitions above:

Integer (= Link to integer link)   ~~ Error. Can not dereference a deeper link.
Integer (= Link to integer link->) ~~ Results to `1`.

The values can not be automatically converted to the deep links. The one-less-depth link can be converted to a deeper link however.

Integer `link` link [= 1]                   ~~ Error.
Integer `link` link [= integer` link [= 1]] ~~ Correct.

Custom Assignment

New values can be assigned only to variables. But assignment statement can be applied to a link also. Such statements interpreted as phrases.

The assignment clause can be declared similarly to the binary operators override. The clause identifier should be a binding statement (<-).

With custom assignment clause declared the link becomes a property:

Property := integer` link (
  Value := ``0 ~~ The property value is kept here. The initial value is `0`.
  = Value

  Set :=> void (
    ~~~
    Value setter.
    ~~~

    Value <- new value
  )

  <*Set> Set (
    ~~~
    Assignment clause.

    It assigns a new value with <set> prototype.
    ~~~

    <New value <- *> New value = *
  )
)

{
  Property->           ~~ Returns `0`.
  Property: value->    ~~ The same as above.
  Property <- 2        ~~ Assigns a new value to `property`.
  Property: value <- 2 ~~ That's what the statement above is actually doing.
}

The value assignment and combined assignment statements can also be used.