Chapter 6. Expressions

An expression produces a value when executed. An algorithm expressed using functions and expressions, rather than sequences of statements is often easier to understand and refactor. Therefore, Ceylon has a highly flexible expressions syntax. Expressions are formed from:

Ceylon expressions are validated for typesafety at compile time. To determine whether an expression is assignable to a program element such as a value or parameter, Ceylon considers the type of the expression (the type of the objects that are produced when the expression is evaluated). An expression is assignable to a program element if the type of the expression is assignable to the declared type of the program element.

Within a dynamic block, an expression may have no type, in the sense that its type can not be determined using static analysis of the code.

6.1. Literal values

Ceylon supports literal values of the following types:

  • Integer and Float,

  • Character, and

  • String.

The types Integer, Float, Character, and String are defined in the module ceylon.language.

Note: Ceylon does not need a special syntax for Boolean literal values, since Boolean is just a class with the cases true and false. Likewise, null is just the singleton value of an anonymous class.

Literal: IntegerLiteral | FloatLiteral | CharacterLiteral | StringLiteral | VerbatimStringLiteral

All literal values are instances of immutable types. The value of a literal expression is an instance of the type. How this instance is produced is not specified here.

6.1.1. Integer number literals

An integer literal, as defined in §2.4.1 Numeric literals, is an expression of type Integer, representing a numeric integer.

Integer five = 5;
Integer mask = $1111_0000;
Integer white = #FFFF;

6.1.2. Floating point number literals

A floating point literal, as defined in §2.4.1 Numeric literals, is an expression of type Float, a floating-point representation of a numeric value.

shared Float pi = 3.14159;

6.1.3. Character literals

A single character literal, as defined in §2.4.2 Character literals, is an expression of type Character, representing a single 32-bit Unicode character.

if (exists ch=string[i], ch == '+') { ... }

6.1.4. Character string literals

A character string literal or verbatim string, as defined in §2.4.3 String literals, is an expression of type String, representing a sequence of Unicode characters.

person.name = "Gavin King";
print("Melbourne\tVic\tAustralia\nAtlanta\tGA\tUSA\nGuanajuato\tGto\tMexico\n");
String verbatim = """A verbatim string can have \ or a " in it."""";

6.2. String templates

A character string template contains interpolated expressions, surrounded by character string fragments.

StringTemplate: StringStart Expression (StringMid Expression)* StringEnd

Each interpolated expression contained in the string template must have a type assignable to Object defined in ceylon.language.

print("Hello, ``person.firstName`` ``person.lastName``, the time is ``Time()``.");
print("1 + 1 = ``1 + 1``");

A string template is an expression of type String.

6.3. Self references

The type of the following expressions depends upon the context in which they appear.

SelfReference: "this" | "super" | "outer"

A self reference expression may not occur outside of a class or interface body.

The immediately containing class or interface for a program element is the class or interface in which the program element occurs, and which contains no other class or interface in which the program element occurs. If there is no such class or interface, the program element has no immediately containing class or interface.

A this, outer, or super self reference must have an immediately containing class or interface. An outer self reference must have an immediately containing class or interface for its immediately containing class or interface.

Note: the keyword package is not an expression, and thus does not have a well-defined type. However, it may be used to qualify and disambiguate a value reference or callable reference. A value reference or callable reference qualified by the keyword package always refers to a toplevel member of the containing package, never to an imported declaration or nested declaration, as defined by §5.1.7 Unqualified reference resolution.

6.3.1. this

The keyword this refers to the current instance, as defined in §8.2.3 Current instance of a class or interface, of the immediately containing class or interface (the class or interface in which the expression appears). Its type is the applied type formed by the immediately containing class or interface with its own type parameters as type arguments.

6.3.2. outer

The keyword outer refers to the current instance, as defined in §8.2.3 Current instance of a class or interface, of the class or interface which immediately contains the immediately containing class or interface. Its type is the applied type formed by this class or interface with its own type parameters as type arguments.

6.3.3. super

The keyword super refers to the current instance of the immediately containing class or interface. Its type is the intersection of the principal instantiation of the immediate superclass for the immediately containing class or interface, as defined in §3.7.4 Principal instantiation of a supertype, with all principal instantiations of immediate superinterfaces of the immediately containing class or interface. A member reference such as super.x may not resolve to a formal declaration, nor to any member inherited from more than one supertype of the intersection type.

As an exception to this, when the keyword super occurs in an extends clause, as specified in §3.3.2 Extension, it refers to the current instance of the class or interface which immediately contains the declaration to which the extends clause belongs. Its type is the intersection of the principal instantiation of the immediate superclass of this containing class or interface, with all principal instantiations of immediate superinterfaces of this containing class or interface.

The keyword super may occur as the first operand of an of operator, in which case the second operand is the principal instantiation of some supertype of the class for the immediately containing class or interface. The expression (super of Type) has type Type. A member reference such as (super of Type).x may not resolve to a formal member, nor to any member inherited from more than one supertype of Type, nor to any member that is refined by the class or any intermediate supertype of the class.

6.4. Anonymous functions

An anonymous function is a function, as specified in §4.7 Functions, with no name, defined within an expression. It comprises one or more parameter lists, followed by an expression or a block of code.

FunctionExpression: ("function" | "void")? Parameters+ (LazySpecifier | Block)

The parameters are the parameters of the function. The lazy specifier or block of code is the implementation of the function.

An anonymous function may be considered void:

  • if the void keyword is specified, the function is a void function, or

  • if the function keyword is specified, the function is not a void function, or,

  • otherwise, the function is void if and only if it is defined using a block in which no return statement with an expression occurs sequentially, as defined in §5.1 Block structure and references.

If the function is not considered void, then its return type is inferred.

The type of an anonymous function expression is the callable type of the function, as specified in §4.7.1 Callable type of a function.

(Value x, Value y) => x<=>y
void (String name) => print(name)
(String string) {
    value mid = string.size / 2;
    return [string[...mid],string[mid+1...]];
}

An anonymous function occurring in the extends clause of a class may not contain a reference to a variable attribute of the class.

Note: evaluation of an anonymous function expression, as defined in §8.4.5 Evaluation of anonymous functions results in instantiation of an object of type Callable. However, the members of this object are never in scope, do not hide other declarations, and are not referenceable from within the anonymous function.

Note: there is almost no semantic difference between the following function declarations:

Float f(Float x)(Float y) => x*y;
Float(Float) f(Float x) => (Float y) => x*y;

The first form is strongly preferred.

6.4.1. Anonymous function parameter type inference

If the type of a parameter of an anonymous function is not declared explicitly, then the type of the parameter may in certain cases be inferred if the anonymous function occurs:

Suppose the type of the ith parameter p of an anonymous function is not declared explicitly, and further suppose that the anonymous function occurs as the argument to a parameter x of some function or class in a direct invocation expression, as defined in §6.6.1 Direct invocations.

Then the type of p may be inferred if either:

  • the function or class has no type parameters, or the invocation has an explicit type argument list, and x is a callable parameter with the same number of parameters as the anonymous function, and with ith parameter q,

  • the function or class has at least one type parameter, and the invocation has no explicit type argument list, and x is a callable parameter with the same number of parameters as the anonymous function, and the type of the ith parameter q of x does not involve any of the type parameters of the generic function or class, or

  • x is a value parameter whose type does not involve any of the type parameters of the generic function or class and represents a function with the same number of parameters as the anonymous function, and with ith parameter q, according to §4.7.1 Callable type of a function.

Then the type of p is inferred to be the type of q in the realization of the function or class, as defined in §3.7.7 Realizations.

Otherwise, suppose the type of the ith parameter p of an anonymous function is not declared explicitly, and further suppose that the anonymous function occurs as the nth argument in the positional argument list of an indirect invocation expression.

Then if the callable type of the invoked expression represents a function whose nth parameter is a callable parameter with the same number of parameters as the anonymous function, according to §4.7.1 Callable type of a function, then the type of p is inferred to be the type of the corresponding ith parameter of this callable parameter.

That is, if the type of the invoked expression is R(*T) where T is a tuple type whose nth element type is P(*S) and S is in turn a tuple type whose ith element type is Q, then Q is the inferred type of p.

6.5. Compound expressions

An atom is a literal or self reference, a string template, a base expression, an iterable or tuple enumeration, an anonymous class expression, a metamodel or reference expression, or a parenthesized expression.

Atom: LiteralExpression | BaseExpression | DelimitedExpression | MetaExpression | SelfReference
LiteralExpression: Literal | StringTemplate
DelimitedExpression: GroupedExpression | Enumeration | ObjectExpression
MetaExpression: Meta | Dec

A primary is formed by recursively forming member expressions, static expressions, invocation expressions, and index expressions from an initial atom, using the operators in the first row of the table of operator precedence and associativity in §6.8.1 Operator precedence.

Primary: Atom | QualifiedExpression | Invocation | IndexedExpression
QualifiedExpression: MemberExpression | ConstructorExpression | StaticExpression

More complex expressions are formed by combining expressions using operators, including assignment operators, as defined in §6.8 Operators, and using inline conditional expressions and anonymous functions.

ValueExpression: Primary | OperatorExpression
Expression: ValueExpression | FunctionExpression | LetExpression | ConditionalExpression

Note: the grammar of operator expressions is defined by the table of operator precedence and associativity in §6.8.1 Operator precedence. Thus, the rules OperatorExpression and IndexedExpression are not defined in BNF.

Parentheses are used for grouping:

GroupedExpression: "(" Expression ")"

A compound expression occurring in a dynamic block, and involving a qualified or unqualified reference with no type, or a reference to a declaration with no type, may also have no type.

In particular, if an operand expression has no type, and the type of the operator expression depends upon the type of the operand, and the operator expression occurs within a dynamic block, then the whole operator expression has no type.

6.5.1. Base expressions

A base expression is an identifier, optionally qualified by the keyword package, with an optional list of type arguments:

BaseExpression: PackageQualifier? (MemberName | TypeName) TypeArguments?

A base expression is either:

  • a reference to a toplevel function, toplevel value, or toplevel class,

  • a reference within the lexical scope of the referenced function, value, constructor, or class, or

  • a reference within the body of the referenced function, value, constructor, or class.

The referenced declaration is determined by resolving the unqualified reference as defined by §5.1.7 Unqualified reference resolution. The unqualified realization for the unqualified reference is determined according to §3.7.7 Realizations.

The type argument list, if any, must conform, as defined by §3.6.3 Type arguments and type constraints, to the type parameter list of the unqualified realization.

If a base expression is a reference to an attribute, method, member class, or member class constructor of a class, the receiving instance is the current instance of that class, as defined by §8.2.3 Current instance of a class or interface. Otherwise, there is no receiving instance.

6.5.2. Member expressions

A member expression is a receiver expression, followed by an identifier, with an optional list of type arguments.

MemberExpression: (Primary ".") (MemberName | TypeName) TypeArguments?

A member expression is a reference to a member of a type: an attribute, method, or member class.

The referenced member is determined by resolving the qualified reference as defined by §5.1.8 Qualified reference resolution. The qualified realization for the qualified reference is determined according to §3.7.7 Realizations.

The type argument list, if any, must conform, as defined by §3.6.3 Type arguments and type constraints, to the type parameter list of the qualified realization.

The receiver expression produces the instance upon which the member is invoked or evaluated. When a member expression is executed, the receiver expression is evaluated to produce the receiving instance which is held until the member is invoked or evaluated, as defined in §8.4 Evaluation, invocation, and assignment.

6.5.3. Constructor expressions

A constructor expression is a base or member expression that references a class with constructors, followed by an identifier, with an optional list of type arguments.

ConstructorExpression: (BaseExpression | MemberExpression) "." MemberName TypeArguments?

A constructor expression is a reference to a constructor of a class.

The referenced member is determined by resolving the qualified reference as defined by §5.1.8 Qualified reference resolution. The qualified realization for the qualified reference is determined according to §3.7.7 Realizations.

The type argument list, if any, must conform, as defined by §3.6.3 Type arguments and type constraints, to the type parameter list of the qualified realization.

If the constructor expression is qualified by a member expression, its receiver expression produces the instance upon which the constructor is invoked. When a constructor expression is executed, the receiver expression is evaluated to produce the receiving instance which is held until the constructor is invoked or evaluated, as defined in §8.4 Evaluation, invocation, and assignment.

6.5.4. Value references

A value reference is a base expression or member expression that references a value declaration or value constructor declaration.

The type of a value reference expression is the type of the realization of the referenced value or value constructor.

A value or value constructor declaration is never generic, so a value reference never has a type argument list.

A value reference that does not occur within any dynamic block may not refer to a value declaration or value parameter with no type.

A value reference which occurs within a dynamic block and which does not reference any statically typed declaration, or which references a value declaration or value parameter with no type, has no type.

If a base expression or member expression does not reference any statically typed declaration, and occurs within a dynamic block, then it is considered a value reference.

6.5.5. Callable references

A callable reference is a base expression, member expression, or constructor expression that references something—a function, class, or callable constructor—that can be invoked or instantiated by specifying a list of arguments.

If a callable reference refers to a class with a default constructor, the callable reference is considered a reference to the default constructor.

A callable reference may be invoked immediately, or it may be passed to other code which may invoke the reference. A callable reference captures the return type and parameter list types of the function or class it refers to, allowing compile-time validation of argument types when the callable reference is invoked.

The type of a callable reference expression is the callable type of the realization of the referenced function, class, or callable constructor.

If a callable reference expression refers to a generic declaration, it must either:

  • have an explicit type argument list,

  • be immediately followed by an argument list, allowing the compiler to infer the type arguments, as defined in §3.6.5 Type argument inference, or,

  • be the immediate child of a constructor expression that is immediately followed by an argument list, allowing the compiler to infer the type arguments, as defined in §3.6.5 Type argument inference, or

  • occur as a listed argument, as defined in §6.6.4 Listed arguments in a positional argument list, or as a specified argument, anonymous argument, or listed argument, as defined in §6.6.8 Named argument lists, in a named argument list, and its type arguments must be inferable as defined below.

A callable reference may not occur as the receiver expression of a member expression.

Note: this restriction exists to eliminate an ambiguity in the interpretation of static expressions such as Person.string and Person.equals.

A callable reference that does not occur within any dynamic block may not refer to a function declaration with no return type.

A callable reference which occurs within a dynamic block and which references a function declaration with no return type, has no type.

Note: in a future release of the language, we would like to add a syntax for obtaining a callable reference to an attribute, something like person.@name, to allow attributes to be passed by reference. This would also allow static references like Person.@name.

If a callable reference f with no explicit type argument list occurs as the argument to a callable parameter p of a function or class in a direct invocation expression, as defined below in §6.6.1 Direct invocations, then the type arguments of f are inferred according to the rules defined in §3.6.5 Type argument inference as if the types of the parameters of p were the types of listed arguments of f in a positional argument list, unless the invoked function or class is generic, and the invocation expression does not itself specify explicit type arguments, in which case any parameter whose type involves a type argument of the invoked function or class is ignored.

If a callable reference f with no explicit type argument list occurs as the argument to a value parameter p of type Return(*Args) in a direct or indirect invocation expression, then the type arguments of f are inferred according to the rules defined in §3.6.5 Type argument inference as if Args were the type of a positional argument list, unless the invocation is a direct invocation expression, and the invoked function or class is generic, and Args involves type parameters of the invoked function or class.

6.5.6. Static expressions

A static expression is a type, optionally qualifier by the keyword package, followed by an identifier, with an optional list of type arguments.

StaticExpression: PackageQualifier? (TypeName TypeArguments? ".")+ (MemberName | TypeName) TypeArguments?

A static expression is a reference to a member of a type: an attribute, method, or member class, or to a constructor of a member class of the type.

The referenced member is determined by resolving the qualified reference as defined by §5.1.8 Qualified reference resolution. The qualified realization for the qualified reference is determined according to §3.7.7 Realizations.

The type argument list, if any, must conform, as defined by §3.6.3 Type arguments and type constraints, to the type parameter list of the qualified realization.

Unlike member expressions, a static expression does not have a receiver expression. All static expressions are callable expressions which accept an argument of the specified type.

A static expression must reference a statically typed declaration with no missing types, even within a dynamic block.

If the qualifying type in a static expression refers to a generic declaration, then either:

  • it must have an explicit type argument list, or

  • the static expression must occur as a listed argument, as defined in §6.6.4 Listed arguments in a positional argument list, or as a specified argument, anonymous argument, or listed argument, as defined in §6.6.8 Named argument lists, in a named argument list, and its type arguments must be inferable as defined below.

If a static expression T.m for a generic type T with no explicit type argument list occurs as the argument to a parameter p of type Return(*Arg) in a direct or indirect invocation expression, then the type arguments of T are inferred according to the rules defined in §3.6.5 Type argument inference as if Arg were the type of a positional argument list, and [T] were the type of a parameter list, unless the invocation is a direct invocation expression, and the invoked function or class is generic, and Arg involves type parameters of the invoked function or class.

6.5.7. Static value references

A static value reference is a static expression that references an attribute declaration or member class value constructor declaration.

List<Anything>.size

The type of a static value reference expression is:

  • X(T) for an attribute whose realization is of type X, and with qualifying type T, or

  • T.X(T) for a member class value constructor whose realization is of type T.X, and with qualifying type T.X.

A value or value constructor declaration is never generic, so a static value reference never ends in a type argument list.

6.5.8. Static callable references

A static callable reference is a static expression that references something—a method or member class, or a callable constructor of a member class—that can be invoked or instantiated.

List<String>.filter
Iterable<Integer>.map<String>

The type of a static callable reference expression is:

  • R(*A)(T) for a method, member class, or member class constructor whose realization has callable type R(*A), and with qualifying type T, or

  • T.X(*A)(T) for a member class constructor whose realization has callable type T.X(*A), and with qualifying type T.X.

If a callable reference expression refers to a generic declaration, it must end in an explicit type argument list.

6.6. Invocation expressions

A callable expression—any expression of type Callable—is invokable. An invocation consists of an invoked expression, together with an argument list and, optionally, an explicit type argument list.

Invocation: Primary Arguments

The invoked expression must be of type R(*P) for some types R and P. Then the type of the invocation expression is simply R.

If the invoked expression has no type, and occurs within a dynamic block, then the whole invocation expression has no type, and the argument list is not type-checked at compile time, unless it is a direct invocation expression.

An invocation expression must specify arguments for parameters of the callable object, either as a positional argument list, or as a named argument list.

Arguments: PositionalArguments | NamedArguments

Every argument list has a type, as specified below in §6.6.7 Positional argument lists and §6.6.8 Named argument lists. If an invocation is formed from a callable expression of type exactly R(*P) and an argument list of type A, then A must be a subtype of P.

6.6.1. Direct invocations

Any invocation expression where the invoked expression is a callable reference expression is called a direct invocation expression of the function, class, or callable constructor to which the callable reference refers.

A direct invocation expression of a callable reference expression that refers to a class or constructor is called a (direct) instantiation expression.

TODO: Should we consider x{y=1;}{z=2;} a legal direct invocation if x has multiple parameter lists?

In a direct invocation expression:

  • the compiler has one item of additional information about the schema of the method or class that is not reified by the Callable interface: the names of the parameters of the function, class, or callable constructor, and therefore named arguments may be used, and

  • type argument inference is possible, as defined in §3.6.5 Type argument inference, since the compiler has access to the type parameters and constraints of the function or class, or of the class to which the callable constructor belongs.

If an invocation expression has a named argument list, it must be a direct invocation.

The type of a direct invocation expression is the return type of the realization of the function, or the type of the realization of the class, as defined in §3.7.7 Realizations.

If the function has no return type, and occurs within a dynamic block, then the whole direct invocation expression has no type.

In a direct invocation expression of a function, class, or callable constructor, the restriction above on the argument list type is equivalent to the following requirements. Given the parameter list of the realization of the function, class, or callable constructor, and the arguments of the direct invocation:

  • for each required parameter, an argument must be given,

  • for each defaulted parameter, an argument may optionally be given,

  • if the parameter list has a variadic parameter of type T+, one or more arguments must be given,

  • if the parameter list has a variadic parameter of type T*, one or more arguments may optionally be given,

  • no additional arguments may be given,

  • for a required or defaulted parameter of type T, the type of the corresponding argument expression must be assignable to T, and

  • for a variadic parameter of type T* or T+, the type of every corresponding argument expression must be assignable to T.

Furthermore, if type argument are inferred, then the inferred type arguments must conform, as defined by §3.6.3 Type arguments and type constraints, to the type parameter list of the realization of the function or class, or class to which the callable constructor belongs.

If an argument expression has no type, or if its parameter has no type, and the invocation occurs within a dynamic block, then the argument is not type-checked at compile time.

An invocation expression that does not occur within any dynamic block may not assign an argument to a value parameter with no type.

6.6.2. Default arguments

When no argument is assigned to a defaulted parameter by the caller, the default argument defined by the parameter declaration of the realization, as defined by §3.6.3 Type arguments and type constraints, of the function, class, or callable constructor is used. The default argument expression is evaluated every time the method is invoked with no argument specified for the defaulted parameter.

This class:

shared class Counter(Integer initialCount=0) { ... }

May be instantiated using any of the following invocations:

Counter()
Counter(1)
Counter {}
Counter { initialCount=10; }

6.6.3. The type of a list of arguments

A list of arguments may be formed from:

  • any number of listed arguments, optionally followed by either

  • a spread argument, or

  • a comprehension.

ArgumentList: ((ListedArgument ",")* (ListedArgument | SpreadArgument | Comprehension))?

Every such list of arguments has a type, which captures the types of the individual arguments in the list. This type is always a subtype of Anything[]. The type of an empty list of arguments is [].

6.6.4. Listed arguments

A listed argument is an expression.

ListedArgument: Expression

If a listed argument is an expression of type T, and a list of arguments has type P with principal instantiation Sequential<Y>, then the type of a new argument list formed by prepending the expression to the first parameter list is Tuple<T|Y,T,P>.

6.6.5. Spread arguments

A spread argument is an expression prefixed by the spread operator *.

SpreadArgument: "*" ValueExpression

The spread operator is parsed with a precedence just lower than the multiplication operator * and just higher than the set union and complement operators | and ~, and is not associative.

Note: this restriction means that the symbol * always has the same precedence, wherever it occurs in the language.

The expression type T must have the principal instantiation {X*} for some type X. We form the sequential type of a spread argument as follows:

  • if the expression type T is an invariant subtype of X[], for some type X then the sequential type of the spread argument is T, or, if not,

  • if the expression type T is an invariant subtype of {X+}, for some type X then the sequential type of the spread argument is [X+], or, otherwise,

  • the expression type T is an invariant subtype of {X*}, for some type X and the sequential type of the spread argument is X[].

When a spread argument with an expression type not assignable to Anything[] is evaluated, the elements of the iterable automatically are packaged into a sequence.

Note: the spread "operator" is not truly an operator in the sense of §6.8 Operators, and so a spread argument is not an expresson. An expression, when evaluated, produces a single value. The spread operator produces multiple values. It is therefore more correct to view the spread operator as simply part of the syntax of an argument list.

The type of a list of arguments containing only a spread argument of sequential type S is simply S.

6.6.6. Comprehensions

A comprehension accepts one or more streams of values and produces a new stream of values. Any instance of Iterable is considered a stream of values. The comprehension has two or more clauses:

  • A for clause specifies a source stream and an iterator pattern, as defined in §5.5.3 for/else, representing the values produced by the stream.

  • An if clause specifies a condition list, as defined in §5.4 Conditions, used to filter the values produced by the source stream or streams.

  • An expression clause produces the values of the resulting stream.

Every comprehension begins with a for or if clause, and ends with an expression clause. There may be any number of intervening for or if clauses. Each clause in the comprehension is considered a child of the clause that immediately precedes it.

Comprehension: ForComprehensionClause | IfComprehensionClause
ForComprehensionClause: "for" ForIterator ComprehensionClause
IfComprehensionClause: "if" ConditionList ComprehensionClause
ComprehensionClause: ForComprehensionClause | IfComprehensionClause | Expression

An expression that occurs in a child clause may refer to iteration variables and condition variables declared by parent clauses. The types of such variables are specified in §5.5 Control structures and assertions.

Note: each child clause can be viewed as a body nested inside the parent clause. The scoping rules for variables declared by comprehension clauses reflects this model.

The type of a list of arguments containing only a comprehension is [T*] where T is the type of the expression which terminates the comprehension, or [T+] if there are no if clauses, and if every for clause has an iterated expression of nonempty type.

Note: a comprehension, like a spread argument, is not considered an expression. An expression, when evaluated, produces a single value. A comprehension produces multiple values, like a spread argument, or like a series of listed arguments. Therefore, a comprehension may only appear in an argument list or an enumeration expression. This is, however, no limitation; we can simply wrap the comprehension in braces in order to get an expression of type {T*}, or in brackets to get an expression of type [T*].

TODO: properly define how expressions with no type occurring in a dynamic block affect comprehensions.

6.6.7. Positional argument lists

When invocation arguments are listed positionally, the argument list is enclosed in parentheses.

PositionalArguments: "(" ArgumentList ")"

The type of the positional argument list is the type of the list of arguments it contains.

6.6.8. Named argument lists

When invocation arguments are listed by name, the argument list is enclosed in braces.

NamedArguments: "{" NamedArgument* ArgumentList "}"

Named arguments may be listed in a different order to the corresponding parameters.

Each named argument in a named argument list is either:

  • an anonymous argument—an expression, with no parameter name explicitly specified,

  • a specified argument—a specification statement where name of the value of function being specified is interpreted as the name of a parameter, or

  • an inline getter, function, or anonymous class declaration, whose name is interpreted as the name of a parameter.

NamedArgument: AnonymousArgument | SpecifiedArgument | InlineDeclarationArgument

Additionally, a named argument list has an ordinary list of arguments, which may be empty. This argument list is interpreted as a single argument to a parameter of type Iterable.

{ initialCapacity=2; "hello", "world" }
{ initialCapacity=people.size; loadFactor=0.8; for (p in people) p.name->p }

Note: in a future release of the language, we would like to be able to assign a local name to an anonymous argument or listed argument, allowing it to be referenced later in the argument list. We might consider this a kind of "let" expression, perhaps.

An inline declaration argument or an ordinary list of arguments occurring in an extends clause of a class may not contain a reference to a variable attribute of the class.

Given a parameter list, and a named argument list, we may attempt to construct an equivalent positional argument list as follows:

  • Taking each argument in the named argument list in turn, on the order they occur lexically:

    • if the argument is anonymous, assign it to the first unassigned parameter of the parameter list, or

    • if the argument is named, assign it to the parameter with that name in the parameter list.

    If, for some argument, there is no unassigned parameter, no parameter with the given name, or the parameter with the given name has already been assigned an argument, construction of the positional argument list fails, and the invocation is not well-typed.

  • Next, if the parameter list has an unassigned parameter of type exactly Iterable<T,N> for some types T and N, then an iterable enumeration expression, as defined in §6.6.12 Iterable and tuple enumeration, is formed from the ordinary list of arguments, and assigned to that parameter.

    If there is no such parameter, and the ordinary list of arguments is nonempty, then construction of the positional argument list fails, and the invocation is not well-typed.

  • Finally, we assign each unassigned defaulted parameter its default argument.

The resulting equivalent positional argument list is formed by ordering the arguments according to the position of their corresponding parameters in the parameter list, and then replacing any inline value, function, or object declarations with a reference to the declaration.

The type of a named argument list is the type of the equivalent positional argument list.

6.6.9. Anonymous arguments

An anonymous argument is just an expression followed by a semicolon.

AnonymousArgument: Expression ";"

The type of the argument is the type of the expression.

{
    Head { title="Hello"; };
    Body {
        Div { "Hello ``name``!" };
    };
}

6.6.10. Specified arguments

A specified argument is a value specification statement or lazy specification statement, as defined in §5.3.3 Specification statements, where the value reference or callable reference is treated as the name of a parameter of the invoked function or class instead of using the usual rules for resolving unqualified names.

SpecifiedArgument: Specification
  • If a specified argument is a value specification statement, its type is the type of the specified expression.

  • If a specified argument is a lazy specification statement with no parameter lists, its type is the type of the specified expression.

  • Otherwise, if it is a lazy specification statement with a parameter list, its type is the callable type formed from the type of the expression, interpreted as a function return type, and the types of its parameter lists, according to §4.7.1 Callable type of a function.

Note: there is an ambiguity here between assignment expressions and specified arguments. This ambiguity is resolved in favor of interpreting the argument as a specified argument. Therefore an anonymous argument in a named argument list may not be an assignment expression.

{ 
    product = getProduct(id); 
    quantity = 1; 
}
{ 
    by(Value x, Value y) => x<=>y;
}

6.6.11. Inline declaration arguments

An inline declaration argument defines a getter, function, or anonymous class, and assigns it to a parameter.

InlineDeclarationArgument: ValueArgument | FunctionArgument | ObjectArgument

An inline getter argument is a streamlined value declaration, as defined in §4.8 Values. The type of the argument is the declared or inferred type of the value.

ValueArgument: ValueHeader (Block | (Specifier | LazySpecifier) ";")

An inline function argument is a streamlined function declaration, as defined in §4.7 Functions. The type of the argument is the callable type of the function, as defined by §4.7.1 Callable type of a function.

FunctionArgument: FunctionHeader (Block | LazySpecifier ";")

An inline anonymous class argument is a streamlined anonymous class declaration, as defined in §4.5.7 Anonymous classes. The type of the argument is the anonymous class type.

ObjectArgument: ObjectHeader ClassBody

A named argument may not have type parameters or annotations.

{
    description = "Total";
    value amount { 
        variable Float total = 0.0;
        for (Item item in items) {
            sum += item.amount;
        }
        return total;
    }
}
{ 
    label = "Say Hello"; 
    void onClick() { 
        say("Hello!"); 
    } 
}
{ 
    function by(Value x, Value y) => x<=>y;
}
{
    object iterator 
            satisfies Iterator<Order> {
        variable value done = false;
        shared actual Order|Finished next() {
            if (done) {
                return finished;
            }
            else {
                done=true;
                return order; 
            }
        }
    }   
}

6.6.12. Iterable and tuple enumeration

An enumeration expression is an abbreviation for tuple and iterable object instantiation. Iterable enumerations are delimited using braces. Tuple enumerations are delimited by brackets.

Enumeration: Iterable | Tuple | DynamicValue
Iterable: "{" ArgumentList "}"
Tuple: "[" ArgumentList "]"

The type of an iterable enumeration expression is:

  • Iterable<Nothing,Null> if there are no argument expressions, or

  • Iterable<U,Nothing> where U, the argument expression list is an invariant suptype of U[].

The type of a tuple enumeration expression is the type of the list of arguments it contains.

{String+} = { "hello", "world" };
[] none = [];
[Float,Float] xy = [x, y];
[Float,Float, String*] xy = [x, y, *labels];

Every argument expression must have a type, even if the enumeration expression occurs in a dynamic block.

An iterable enumeration expression occurring in an extends clause of a class may not contain a reference to a variable attribute of the class.

6.6.13. Dynamic enumerations

A dynamic enumeration expression creates a new object with no class by enumerating its members, allowing interoperation with dynamically typed native code.

DynamicValue: "dynamic" "[" NamedArgument* ArgumentList "]"

A dynamic enumeration expression has no type.

Any argument names may be specified in the named argument list.

A dynamic enumeration expression must occur inside a dynamic block.

The semantics of this construct are platform-dependent and beyond the scope of this specification.

6.7. Conditional expressions, let expressions, and anonymous class expressions

A conditional expression resembles a control structure but is part of the expression syntax, and evaluates to a value. A conditional expression comes in one of two forms:

  • an if/then/else expression resembles the if/else conditional defined in §5.5.1 if/else, and

  • a switch/case/else expression resembles the switch/case/else conditional defined in §5.5.2 switch/case/else.

ConditionalExpression: IfElseExpression | SwitchCaseElseExpression

A let expression allows inline definition of a reference within an expression.

An inline class is an anonymous class defined within an expression.

6.7.1. if/then/else expressions

An if/then/else expression has a condition list, as defined in §5.4 Conditions, a then expression, and an else expression. The else expression is not optional.

IfElseExpression: "if" ConditionList ThenExpression ElseExpression

The type of an if/then/else expression with then expression of type X and else or else if expression of type Y is X|Y.

ThenExpression: "then" Expression
ElseExpression: "else" Expression

The expression following then or else is parsed with precedence just higher than the || operator, and just lower than the then and else operators, that is, between the layers 3 and 4 defined in §6.8.1 Operator precedence.

Alternatively, the expression following then or else may be an if/then/else expression or a let expression.

Note: the expression following then or else may not be a switch/case/else expression, since that would introduce an ambiguity related to the optional else clause of the switch/case/else expression.

if (exists lang) then lang.name else "Ceylon"

6.7.2. switch/case/else expressions

A switch/case/else expression has a switch expression or inline variable, a list of case expressions, and, optionally, an else expression.

SwitchCaseElseExpression: Switch CaseExpression+ ElseExpression

The type of a switch/case/else expression with case expressions of type X1, X2, ..., Xn and else expression of type Y is X1|X2|...|Xn|Y.

CaseExpression: "case" CaseCondition Expression

The expression following case or else is parsed with precedence just higher than the || operator, and just lower than the then and else operators, that is, between the layers 3 and 4 defined in §6.8.1 Operator precedence.

Alternatively, the expression following then or else may be an if/then/else expression or a let expression.

Each case expression includes a value case or type case, as defined in §5.4.4 Case conditions. Just like in a switch/case/else conditional statement:

  • all cases must be disjoint, and

  • if there is no else expression, the cases must be exhaustive.

switch (seq) case (null) "null" case (is []) "empty" else "nonempty"

6.7.3. Let expressions

A let expression comprises a pattern list, followed by an expression involving the pattern variables that occur in the listed patterns.

LetExpression: "let" PatternList Expression

The expression is parsed with precedence just higher than the || operator, and just lower than the then and else operators, that is, between the layers 3 and 4 defined in §6.8.1 Operator precedence.

Alternatively, the expression may be an if/then/else expression or another let expression.

A pattern list is enclosed in parentheses.

PatternList: "(" PatternListElement ("," PatternListElement)* ")"

Each element of the pattern list is a pattern, as defined in §5.2.2 Patterns, followed by a specified expression. The patterned type is the type of the specified expression.

PatternListElement: Pattern Specifier

The pattern variables that occur in the pattern list are considered in scope in the expression that follows the pattern list. Furthermore, a specified expression in the pattern list may refer to a pattern variable declared by an earlier element in the pattern list.

let ([x,y] = loc, d = sqrt(x^2+y^2)) [x/d, y/d]

6.7.4. Inline anonymous class expressions

An inline anonymous class expression resembles an anonymous class declaration as defined in §4.5.7 Anonymous classes. The expression defines the schema, supertypes, and implementation of a class. It does not specify a type name. Instead, the type has a name assigned internally by the compiler that is not available at compilation time.

ObjectExpression: "object" ObjectInheritance ClassBody

The class:

  • is implicitly final, and

  • may not declare default members.

The type of an inline anonymous class expression is the intersection of the class type it extends with all interface types it satisfies. The type of the inline anonymous class itself is not accessible outside the body of the inline anonymous class expression.

object 
        satisfies {Integer+} {
    iterator() => object 
            satisfies Iterator<Integer> {
        variable value current = 0;
        next() => current++;
    };
}

An inline anonymous class expression occurring in an extends clause of a class may not contain a reference to a variable attribute of the class.

6.8. Operators

Operators are syntactic shorthand for more complex expressions involving invocation, evaluation, or instantiation. There is no support for user-defined operator overloading:

  • new operator symbols may not be defined outside of the operators specified below, and

  • the definition of the operators specified below may not be changed or overloaded.

However, many of the operators below are defined in terms of default or formal methods or attributes. So, within well-defined limits a concrete implementation may customize the behavior of an operator. This approach is called operator polymorphism.

Some examples:

Float z = x * y + 1.0;
even = n % 2 == 0;
++count;
Integer j = i++;
if ( x > 100 || x < 0 ) { ... }
User user = users[userId] else guest;
List<Item> firstPage = results[0..20];
for (n in 0:length) { ... }
if (char in 'A'..'Z') { ... }
String[] names = people*.name;
this.total += item.price * item.quantity;
Float vol = length^3;
Vector scaled = scale ** vector;
map.contains(person.name->person);
if (!document.internal || user is Employee) { ... }

6.8.1. Operator precedence

There are 19 distinct operator precedence levels, but these levels are arranged into layers in order to make them easier to predict.

  • Operators in layer 1 produce, transform, and combine values.

  • Operators in layer 2 compare or predicate values, producing a Boolean result.

  • Operators in layer 3 are logical operators that operate upon Boolean arguments to produce a Boolean value.

  • Operators in layer 4 perform assignment and conditional evaluation.

Within each layer, postfix operators have a higher precedence than prefix operators, and prefix operators have a higher precedence than binary operators.

There is a single exception to this principle: the binary exponentiation operator ^ has a higher precedence than the prefix operators + and -. The reason for this is that the following expressions should be equivalent:

-x^2       //means -(x^2)
0 - x^2    //means 0 - (x^2)

This table defines the relative precedence of the various operators, from highest to lowest, along with associativity rules:

Table 6.1. 

OperationsOperatorsTypeAssociativity
Layer 1
Member invocation and selection, index, subrange:., *., ?., (), {}, [], [:], [..], [...]Binary / N-aryLeft
Postfix increment and decrement:++, --Unary postfixLeft
Prefix increment and decrement:++, --Unary prefixRight
Exponentiation:^BinaryRight
Negation:+, -Unary prefixRight
Set intersection:&BinaryLeft
Set union and complement:|, ~BinaryLeft
Multiplication, division, remainder:*, /, %BinaryLeft
Scale:**BinaryRight
Addition, subtraction:+, -BinaryLeft
Range and entry construction:.., :, ->BinaryNone
Layer 2
Existence, emptiness:exists, nonemptyUnary postfixNone
Comparison, containment, assignability, inheritance:<=>, <, >, <=, >=, in, is, ofBinary (and ternary)None
Equality, identity:==, !=, ===BinaryNone
Layer 3
Logical not:!Unary prefixRight
Logical and:&&BinaryLeft
Logical or:||BinaryLeft
Layer 4
Conditionals:then, elseBinaryLeft
Assignment:=, +=, -=, *=, /=, %=, &=, |=, ~=, &&=, ||=BinaryRight

It's important to be aware that in Ceylon, compared to other C-like languages, the logical not operator ! has a very low precedence. The following expressions are equivalent:

!x.y == 0.0  //means !(x.y == 0.0)
x.y != 0.0

6.8.2. Operator definition

The following tables define the semantics of the Ceylon operators. There are six basic operators which do not have a definition in terms of other operators or invocations:

  • the member selection operator . separates the receiver expression and member name in a member expression, as defined above in §6.5.2 Member expressions,

  • the argument specification operators () and {} specify the argument list of an invocation, as defined in §6.6 Invocation expressions and §8.4.4 Invocation,

  • the assignment operator = assigns a new value to a variable and returns the new value after assignment, as defined in §8.4.3 Assignment,

  • the identity operator === evaluates to true if its argument expressions evaluate to references to the same object, as defined in §8.1 Object instances, identity, and reference passing, or to false otherwise,

  • the assignability operator is evaluates to true if its argument expression evaluates to an instance of a class, as defined in §8.1 Object instances, identity, and reference passing, that is a subtype of the specified type, or to false otherwise, and

  • the coverage operator of narrows or widens the type of an expression to any specified type that covers the expression type, as defined by §3.4.1 Coverage, without affecting the value of the expression.

All other operators are defined below in terms of other operators and/or invocations.

6.8.3. Basic invocation and assignment operators

These operators support method invocation and attribute evaluation and assignment.

Table 6.2. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Invocation
lhs.membermember Xa member of X, of type TT
lhs(x,y,z) or lhs{a=x;b=y;}invoke T(*P)argument list of type PT
Assignment
lhs = rhsassignvariable of type XXX
Coverage
lhs of TypeofXa literal type T that covers XT

6.8.4. Equality and comparison operators

These operators compare values for equality, order, magnitude, or membership, producing boolean values.

Table 6.3. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Equality and identity
lhs === rhsidenticalX given X satisfies IdentifiableY given Y satisfies Identifiable where X&Y is not NothingBoolean
lhs == rhsequallhs.equals(rhs)ObjectObjectBoolean
lhs != rhsnot equal!lhs.equals(rhs)ObjectObjectBoolean
Comparison
lhs <=> rhscomparelhs.compare(rhs)Comparable <T>TComparison
lhs < rhssmallerlhs.compare(rhs)==smallerComparable <T>TBoolean
lhs > rhslargerlhs.compare(rhs)==largerComparable <T>TBoolean
lhs <= rhssmall aslhs.compare(rhs)!=largerComparable <T>TBoolean
lhs >= rhslarge aslhs.compare(rhs)!=smallerComparable <T>TBoolean
Containment
lhs in rhsinlet (x=lhs) rhs.contains(x)ObjectCategoryBoolean
Assignability
rhs is Typeis any type which is not a subtype of T, whose intersection with T is not Nothingany literal type TBoolean

TODO: Should we have allow the operators <= and >= to handle partial orders? A particular usecase is Set comparison.

A bounded comparison is an abbreviation for two binary comparisons:

  • l<x<u means let (t=x) l<t && t<u,

  • l<=x<u means let (t=x) l<=t && t<u,

  • l<x<=u means let (t=x) l<t && t<=u, and

  • l<=x<=u means let (t=x) l<=t && t<=u

for expressions l, u, and x.

These abbreviations have the same precedence as the binary < and <= operators, and, like the binary forms, are not associative.

6.8.5. Logical operators

These are the usual logical operations for boolean values.

Table 6.4. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Logical operators
!rhsnotif (rhs) then false else true BooleanBoolean
lhs || rhsconditional orif (lhs) then true else rhsBooleanBooleanBoolean
lhs && rhsconditional andif (lhs) then rhs else falseBooleanBooleanBoolean
Logical assignment
lhs ||= rhsconditional orif (lhs) then true else lhs=rhsvariable of type BooleanBooleanBoolean
lhs &&= rhsconditional andif (lhs) then lhs=rhs else falsevariable of type BooleanBooleanBoolean

6.8.6. Operators for handling null values

These operators make it easy to work with optional expressions.

Table 6.5. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Existence
lhs existsexistsif (exists lhs) then true else falseany type whose intersections with Object and Null are not Nothing Boolean
lhs nonemptynonemptyif (nonempty lhs) then true else falseany subtype of Anything[]? whose intersections with [] and [Nothing+] are not Nothing Boolean
Nullsafe invocation
lhs?.membernullsafe attributeif (exists lhs) then lhs.member else nullX?an attribute of type T of XT?
lhs?.membernullsafe method X?a method of callable type T(*P) of X with exactly one parameter listT?(*P)

6.8.7. Correspondence, subrange, and stream operators

These operators provide a simplified syntax for accessing values of a Correspondence, for obtaining subranges of Ranged objects, and for spreading member access over a stream.

Table 6.6. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Keyed item access
lhs[index]lookuplhs.get(index)Correspondence<X,Y>XY?
Subranges
lhs[from:length]measured subrangelhs.measure(from,length)Ranged<X,Y,Z>X, IntegerZ
lhs[from..to]spanned subrangelhs.span(from,to)Ranged<X,Y,Z>X, XZ
lhs[from...]upper spanned subrangelhs.spanFrom(from)Ranged<X,Y,Z>XZ
lhs[...to]lower spanned subrangelhs.spanTo(to)Ranged<X,Y,Z>XZ
Spread invocation
lhs*.attributespread attribute[*lhs.map(X.attribute)]Iterable<X,N>attribute of X of type T[T*] or [T+]
lhs*.methodspread methodcompose((Iterable<T,N> ts)=>[*ts], lhs.spread(X.method))Iterable<X,N>method of X of callable type T(*P) with exactly one parameter list[T*](*P) or [T+](*P)
Spread multiplication
lhs ** rhsscalerhs.scale(lhs)XScalable<X,Y>Y

Operands within brackets in any subrange operator are parsed as if they were operands of the .. or : operators.

Note: an ambiguity exists in interpretation of expressions like map[n..m] and map[0:l], where the expression could in principle be interpreted as a lookup operator applied to a range constructor. This ambiguity is always resolved in favor or interpreting the expression as a subrange operator.

There are two special cases related to sequences. A type X is a sequence type if X is a subtype of Sequential<Anything>.

For any sequence type X with principal instantiation [E*] and integer n, we can form the nth tail type, Xn, of X as follows:

  • for every i<=0, Xi is X,

  • for every i>0, if Xi has the principal instantiation Tuple<Ui,Fi,Ti> then X(i+1) is Ti, or, if Xi has principal instantiation [Fi*] then X(i+1) is [Fi*], or, otherwise, if Xi is [], then X(i+1) is also [].

For any sequence type X and integer n, we can form the nth element type, En, of X as follows:

  • if n>=0 and Xn has the principal instantiation [Fn+] then En is Fn, or,

  • otherwise, Xn has the principal instantiation [Fn*] and En is Fn?.

Then the two special cases are:

  • The type of an expression of form x[n] where x is of the sequence type X and n is an integer literal is En.

  • The type of an expression of form x[n...] where x is of the sequence type X and n is an integer literal is Xn if Xn is an instantiation of Tuple, [Fn+] if Xn has the principal instantiation [Fn+], or [Fn*] if Xn has the principal instantiation [Fn*].

6.8.8. Operators for creating objects

These operators simplify the syntax for instantiating certain commonly used built-in types.

Table 6.7. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Range and entry constructors
lhs..rhsspanned rangespan(lhs, rhs)T given T satisfies Enumerable<T>TRange<T>
lhs:rhsmeasured rangemeasure(lhs,rhs)T given T satisfies Enumerable<T>IntegerRange<T>|[]
lhs->rhsentryEntry(lhs, rhs)U given U satisfies ObjectVEntry<U,V>

6.8.9. Conditional operators

Two special operators allow emulation of the famous ternary operator of C-like languages.

Table 6.8. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Conditionals
lhs then rhsthenif (lhs) then rhs else nullBooleanT given T satisfies ObjectT?
lhs else rhselseif (exists lhs) then lhs else rhsU such that null is UVU&Object|V

6.8.10. Arithmetic operators

These are the usual mathematical operations for all kinds of numeric values.

Table 6.9. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Increment, decrement
++rhssuccessorrhs=rhs.successor variable of type Ordinal<T>T
--rhspredecessorrhs=rhs.predecessor variable of type Ordinal<T>T
lhs++incrementlet (x = lhs, _ = lhs = lhs.successor) xvariable of type Ordinal<T> T
lhs--decrementlet (x = lhs, _ = lhs = lhs.predecessor) xvariable of type Ordinal<T> T
Numeric operators
+rhs rhs Invertible <I>I
-rhsnegationrhs.negated Invertible <I>I
lhs + rhssumlhs.plus(rhs)Summable<X>XX
lhs - rhsdifferencelhs.minus(rhs)Invertible <X>XX
lhs * rhsproductlhs.times(rhs)Numeric<X>XX
lhs / rhsquotientlhs.divided(rhs)Numeric<X>XX
lhs % rhsremainderlhs.remainder(rhs)Integral<X>XX
lhs ^ rhspowerlhs.power(rhs)Exponentiable <X,Y>YX
Numeric assignment
lhs += rhsaddlhs=lhs.plus(rhs)variable of type Summable<N>NN
lhs -= rhssubtractlhs=lhs.minus(rhs)variable of type Invertible <N>NN
lhs *= rhsmultiplylhs=lhs.times(rhs)variable of type Numeric<N>NN
lhs /= rhsdividelhs=lhs.divided(rhs)variable of type Numeric<N>NN
lhs %= rhsremainderlhs=lhs.remainder(rhs)variable of type Integral<N>NN

Arithmetic operators automatically widen from Integer to Float when necessary. If one operand expression is of static type Integer, and the other is of type Float, the operand of type Integer is widened to a Float in order to make the operator expression well-typed. Widening is performed by evaluating the attribute float defined by Integer.

Note: this is the only circumstance in the language where implicit type conversion occurs. In fact, it is more correct to view this behavior as an instance of operator overloading than as an implicit type conversion. Implicit widening does not occur when an expression of type Integer is merely assigned to the type Float, since such behavior would result in ambiguities when generics come into play.

6.8.11. Set operators

These operators provide traditional mathematical operations for sets.

Table 6.10. 

ExampleNameDefinitionLHS typeRHS typeReturn type
Set operators
lhs | rhsunionlhs.union(rhs)Set<X>Set<Y>Set<X|Y>
lhs & rhsintersectionlhs.intersection(rhs)Set<X>Set<Y>Set<X&Y>
lhs ~ rhscomplementlhs.complement(rhs)Set<X>Set<Object>Set<X>
Set assignment
lhs |= rhsunionlhs=lhs|rhsvariable of type Set<X>Set<X>Set<X>
lhs &= rhsintersectionlhs=lhs&rhsvariable of type Set<X>Set<Object>Set<X>
lhs ~= rhscomplementlhs=lhs~rhsvariable of type Set<X>Set<Object>Set<X>

6.9. Metamodel expressions

A metamodel expression is a reference to a type, a class, a function, a value, or a constructor. It evaluates to a metamodel object whose static type captures, respectively:

  • the type itself,

  • the callable type of the class,

  • the callable type of the function,

  • the type of the value,

  • the type of the value constructor, or

  • the callable type of the callable constructor.

Meta: TypeMeta | BaseMeta | MemberMeta | ConstructorMeta

A type metamodel expression is a type, as defined by §3.2 Types, surrounded by backticks.

TypeMeta: "`" Type "`"

The type may or may not be a reference to a class or interface.

Class<Person,[Name]> personClass = `Person`;
Interface<List<String>> stringListInterface = `List<String>`;
UnionType<Integer|Float> numberType = `Number`;
Type<Element> elementType = `Element`;

A base metamodel expression is a member name, with an optional list of type arguments, surrounded by backticks.

BaseMeta: "`" PackageQualifier? MemberName TypeArguments? "`"

A base metamodel expression is a reference to a value or function. The referenced declaration is determined according to §5.1.7 Unqualified reference resolution.

A member metamodel expression is a qualifier, followed by a member name, with an optional list of type arguments, surrounded by backticks.

MemberMeta: "`" PrimaryType "." MemberName TypeArguments? "`"

The member metamodel expression is qualified by a type, as defined by §3.2 Types.

A member metamodel expression is a reference to an attribute or method of the type identified by the qualifier. The member is resolved as a member of the type according to §5.1.8 Qualified reference resolution.

Function<Float,[{Float+}]> sumFunction = `sum<Float>`;
Attribute<Person,String> personNameAttribute = `Person.name`;
Method<Person,Anything,[String]> personSayMethod = `Person.say`;
Attribute<\Isystem,Integer> systemMillis = `\Isystem.milliseconds`;

A constructor metamodel expression is a qualifier, followed by a constructor name, with an optional list of type arguments, surrounded by backticks.

ConstructorMeta: "`" PrimaryType "." ()MemberName | TypeName) TypeArguments? "`"

The constructor metamodel expression qualifier is a type that is an instantiation of a class.

A constructor metamodel expression is a reference to a constructor or of the class identified by the qualifier. The constructor is resolved as a member of the class according to §5.1.8 Qualified reference resolution.

Type argument inference is impossible in a metamodel expression, so type arguments must be explicitly provided for every generic declaration.

6.9.1. Type of a metamodel expression

The type of a metamodel expression depends upon the kind of declaration referenced:

  • for a toplevel value of type R, the type is Value<R>,

  • for a toplevel function of callable type R(*P), the type is Function<R,P>,

  • for a toplevel class of callable type R(*P), the type is Class<R,P>,

  • for a callable constructor of a toplevel class of callable type R(*P), the type is CallableConstructor<R,P>,

  • for a value constructor of a toplevel class of type R, the type is ValueConstructor<R>,

  • for a class nested in a block of callable type R(*P), the type is Class<R,Nothing>, and

  • for a toplevel interface or interface nested in a block of type R, the type is Interface<R>.

Note: members of anonymous classes are treated as toplevels here.

Furthermore, given a member of a type T:

  • for an attribute of type R, the type is Attribute<T,R>,

  • for a method of callable type R(*P), the type is Method<T,R,P>,

  • for a member class of callable type R(*P), the type is MemberClass<T,R,P>, and

  • for a callable constructor of a member class of callable type R(*P), the type is MemberClassCallableConstructor<T,R,P>, and

  • for a value constructor of a member class of type R, the type is MemberClassValueConstructor<T,R>, and

  • for a value of a member class of type R, the type is Attribute<T,R>, and

  • for a nested interface of type R, the type is MemberInterface<T,R>.

Finally:

  • for a union type T, the type is UnionType<T>,

  • for an intersection type T, the type is IntersectionType<T>,

  • for the type Nothing, the type is Type<Nothing>, and

  • for a type parameter T, the type is Type<T>.

If a type alias occurs inside a typed metamodel expression, it is replaced by its definition, after substituting type arguments, before determining the type of the metamodel expression.

6.10. Reference expressions

A reference expression is a reference to a program element and evaluates to a detyped metamodel of the program element. Reference expressions are used primarily in annotations, especially the documentation annotations listed in §7.4.2 Documentation. A reference expression may refer to:

  • a class, interface, type alias, or type parameter,

  • a function or value,

  • a constructor, or

  • a package or module.

Dec: TypeDec | MemberDec | ConstructorDec | PackageDec | ModuleDec

6.10.1. Declaration references

Declaration reference expressions may be qualified by a member declaration qualifier, a sequence of identifiers identifying a class or interface declaration or an anonymous class declaration:

MemberDecQualifier: ( (TypeName | MemberName) "." )+

Each identifier in the member declaration qualifier is the name of a class, interface, or anonymous class.

A class reference expression, interface reference expression, alias reference expression, or type parameter reference expression is an optional member declaration qualifier, followed by the name of a class or anonymous class, interface, alias, or type parameter, with the keyword class, interface, alias, or given, respectively, surrounded by backticks.

TypeKeyword: "class" | "interface" | "alias" | "given"
TypeDec: "`" TypeKeyword ( PackageQualifier? MemberDecQualifier? (TypeName | MemberName) )? "`"

For a class or interface reference expression, the name of the class or interface is optional. In this case, the class or interface reference is to the immediately containing class or interface, if any, as defined in §6.3 Self references. For alias or type parameter reference expressions, the name of the alias or type parameter is required.

ClassDeclaration thisClass = `class`;
ClassDeclaration personClass = `class Person`;
ClassDeclaration thisInterface = `interface`;
InterfaceDeclaration stringListInterface = `interface List`;
AliasDeclaration numberAlias = `alias Number`;
TypeParameter elementTypeParameter = `given Element`;
CallableConstructorDeclaration arrayOfSizeConstructor = `new Array.ofSize`;

A value reference expression or function reference expression is an optional member declaration qualifier, followed by the name of a function, value, value constructor, or anonymous class, with the keyword value or function, surrounded by backticks.

MemberKeyword: "value" | "function"
MemberDec: "`" MemberKeyword PackageQualifier? MemberDecQualifier? MemberName "`"

A constructor reference expression is a member declaration qualifier, followed by the name of a callable constructor, with the keyword new, surrounded by backticks.

ConstructorKeyword: "new"
ConstructorDec: "`" ConstructorKeyword PackageQualifier? MemberDecQualifier TypeName "`"

A reference expression is a reference to a declaration. The referenced declaration is determined according to §5.1.7 Unqualified reference resolution and §5.1.8 Qualified reference resolution. The kind of the referenced declaration must match the kind of reference indicated by the keyword.

ValueDeclaration personNameAttribute = `value Person.name`;
FunctionDeclaration personSayMethod = `function Person.say`;
FunctionDeclaration processWriteMethod = `function process.write`;
ClassDeclaration processClass = `class process`;

6.10.2. Package and module references

A package reference expression is a package name, as defined by §4.1.2 Packages, with the keyword package, surrounded by backticks.

PackageDec: "`" "package" FullPackageName? "`"

The package name must refer to a package from which an import statement in the same compilation unit may import declarations, as defined by §4.2 Imports.

If there is no explicit package name, the package reference is to the package in which the package reference expression occurs.

Package currentPackage = `package`;
Package modelPackage = `package ceylon.language.meta.model`;

A module reference expression is a module name, as defined by §9.3.1 Module names and version identifiers, with the keyword module, surrounded by backticks.

ModuleDec: "`" "module" FullPackageName? "`"

The module name must refer to the module to which the compilation unit belongs, as specified by §9.2 Source layout, or to a module imported by the module to which the compilation unit belongs, as defined by §9.3.10 Module descriptors.

If there is no explicit module name, the module reference is to the package in which the module reference expression occurs.

Module currentModule = `module`;
Module languageModule = `module ceylon.language`;

6.10.3. Type of a reference expression

The type of a reference expression depends upon the kind of program element referenced:

  • for a module, the type is Module,

  • for a package, the type is Package,

  • for a reference, the type is ReferenceDeclaration,

  • for any other value, the type is ValueDeclaration,

  • for a function, the type is FunctionDeclaration,

  • for a callable constructor, the type is CallableConstructorDeclaration,

  • for a value constructor, the type is ValueConstructorDeclaration,

  • for a type parameter, the type is TypeParameter,

  • for a type alias declared using the keyword alias, the type is AliasDeclaration,

  • for a class with an initializer parameter list, or for any class alias, the type is ClassWithInitializerDeclaration,

  • for a class with constructors, the type is ClassWithConstructorsDeclaration, and

  • for an interface or interface alias, the type is InterfaceDeclaration.

For a reference to an anonymous class, the type depends upon the keyword, class, or value, specified in the reference expression:

  • for a class reference expression, the type is ClassDeclaration, but

  • for a value reference expression, the type is ValueDeclaration.