Note: information on this page refers to Ceylon 1.1, not to the current release.

Value and attribute declarations

There are two kinds of value:

  • A reference holds state, that is, a reference to another object.
  • A value defined as a getter (with, optionally, a matching setter) does not directly hold state, instead it defines how a "derived" value is computed.

Crucially, users of the value cannot distinguish a reference value from a getter value.

When a value is a member of a type, is it called an attribute.

Usage

A trivial reference:

String trivalReference = "Alice";

A trivial getter:

String trivialSpecifiedGetter => "Bob";

A trivial getter and setter:

Integer trivialValueWithBlock {
    return i+10;
}
assign trivialValueWithBlock {
    i = trivialValueWithBlock-10;
}

The setter uses the assign keyword, and has the same name as the corresponding getter.

The general form of a value can look like any of these:

ANNOTATIONS
TYPE exampleReference = INITIAL-EXPRESSION;
// or
ANNOTATIONS
TYPE exampleGetterSpecified => GETTER-EXPRESSION;
// or
ANNOTATIONS
TYPE exampleGetterBlock {
    GETTER-BODY
}
// or
ANNOTATIONS
TYPE exampleGetterBlock {
    GETTER-BODY
}
assign exampleGetterBlock {
    SETTER-BODY
}

Where:

Description

Value type

A non-local value declaration always specifies the type of the value.

Type inference

Local value and attribute declarations don't need to explictly declare a type, but can instead use type inference via the value keyword.

void f() {
    value string = ""; //inferred type String
}

Reference values

Reference values are just holders of state.

A toplevel reference represents a "global constant" (or variable).

A reference attribute declared within a class body represents state associated with an instance of the class (similar to a field in other programming languages).

A local reference (that is, a reference declared within a block) represents state associated with execution of that block (similar to a local variable in other programming languages).

Reference values are computed when the value is specified or assigned and the exact same instance returned thereafter (unless the value is variable and is reassigned).

variable reference values

If a reference value is annotated variable, it can be assigned more than once. Otherwise it must be specified exactly once.

The specification or assignment must occur before its first use.

Getters

A getter defines a derived value that is computed every time the value is required (unlike a reference value).

From the point of view of the code evaluating a value, it is impossible to determine whether the value is implemented as a reference or as a getter. It's even possible to refine a reference with a getter, or vice versa.

Like function declarations, you can either use a block of statements or the fat arrow (=>) syntax if the value can be computed from a single expression.

Setters

A setter defines what to do when a derived variable value is assigned.

From the point of view of the code assigning a value, it is impossible to determine whether the value is implemented as a reference or as a setter. It's even possible to refine a variable reference with a getter/setter pair, or vice versa.

Every setter must have a corresponding getter with the same name.

The getter declaration must occur earlier in the body containing the setter declaration.

Attribute receiver

Attribute evaluations have a 'receiver', an instance of the type that declares the method. Within the getter or setter body, the expression this refers to this receiving instance.

A top level value does not have a receiver.

Different kinds of value

late values

A value can be declared late in which case the typechecker's definite specification checks are not performed.

formal and default attributes

An attribute declaration may be annotated formal or default. A formal or default attribute must also be annotated shared.

A formal attribute does not specify an implementation. A formal attribute must be refined by concrete classes which inherit the containing class or interface.

A default attribute may be refined by types which inherit the containing class or interface.

shared values

A toplevel value declaration, or a value declaration nested inside the body of a containing class or interface, may be annotated shared.

  • A toplevel shared value is visible wherever the package that contains it is visible.
  • A shared value nested inside a class or interface is visible wherever the containing class or interface is visible.

Metamodel

Value declarations can be manipulated at runtime via their representation as ValueDeclaration instances. An applied function (i.e. with all type parameters specified) corresponds to either a Value or Attribute model instance.

See also