The Ceylon metamodel base package.

The Ceylon metamodel allows you to:

  • dynamically inspect modules, packages, functions, values and types,
  • invoke functions and class initialisers, or read and write values, and
  • inspect the annotations on program elements.

A little bit of terminology

There are a few concepts and terms in the Ceylon metamodel API that you should be familiar with:

  • A declaration is the definition of a Ceylon construct, such as a module, package, value, function or class. Declarations are singletons: there is only a single instance of a given class declaration, for example. You can inspect declarations to get information about how they were defined by their author. You cannot directly invoke function or class declarations, but you can apply them to get a model that you can invoke.
  • A model is a Ceylon definition that represents a declaration where all the type variables have been bound to closed type values. You can query models for their member models and you can directly invoke models.
  • A closed type is a type which does not contain any unbound type variables.
  • An open type is a type which may contain unbound type variables.

For example, given the following Ceylon program:

shared abstract class MyList<T>() satisfies List<T>{}

The declaration of MyList represents the class declaration and contains the information that it is abstract and that it satisfies the List<T> open type. That type is open because it contains an unbound type variable T, which is not bound when we inspect the MyList class declaration.

Given an particular instance of MyList, we can query its (closed) type with the type function, and we obtain a closed type representing (for example) MyList<Integer>. Object instances necessarily have a closed type at runtime, since in order to instantiate an object, all type arguments must be provided and known during instantiation, so the type of an object instance at runtime is necessarily a closed type: they cannot contain unbound type variables.

Closed types that represent class or interfaces are also models. For example, the closed type of our MyList<Integer> instance is both a closed type and a class model: you can query its satisfied types and find that it satisfies List<Integer> closed type and model (as opposed to the class declaration of MyList which satisfies the List<T> open type). You can also invoke that model to obtain a new instance of MyList<Integer>.

Model and declaration literals

Ceylon supports getting declaration values using either the declaration API or using declaration literals:

  • `module ceylon.file` returns the Module declaration which corresponds to the ceylon.file module you imported in your module descriptor.
  • `package ceylon.language.meta` returns the Package declaration from your current module imports.
  • `interface List` returns the InterfaceDeclaration for the List type.
  • `class Integer` returns the ClassDeclaration for the Integer type.
  • `function type` returns the FunctionDeclaration for the type function. Similarly `function List.shorterThan` for methods.
  • `value modules` returns the ValueDeclaration for the modules value. Similarly `function List.size` for attributes.
  • `alias AliasName` returns the AliasDeclaration for the AliasName type alias.
  • `given T` returns the TypeParameter for the T type parameter.

Note that declaration literals cannot have type arguments specified on types or methods, as declarations are not types.

You can also get access to closed types and model using either the model API or using literals:

  • `List<Integer>` returns the Interface model and closed type for the List type applied with the Integer type argument.
  • `Integer` returns the Class model and closed type for the Integer type.
  • `type<Integer>` returns the Function model for the type function applied with the Integer type argument. Similarly `List<Integer>.shorterThan` for method models.
  • `modules` returns the Value model for the modules value. Similarly `List<Integer>.size` for attribute models.
  • `A & B` returns a IntersectionType for the A & B intersection type.
  • `A | B` returns a UnionType for the A | B union type.
  • `T` returns a Type representing the runtime type argument value for the T type parameter.

Notice that all model and close type literals must be applied with all required type arguments.

Accessing the metamodel using the API

Aside from declaration and model literals there are several ways you can start using the metamodel API:

  • The modules object contains a list of all currently loaded Module declarations. Note that these contain even modules you did not import as it contains all transitive dependencies, and may contain multiple different versions of the same module.
  • The type function will return the closed type of the given instance, which can only be a ClassModel since only classes can be instantiated.
  • The typeLiteral function is the functional equivalent to closed type literals: it turns a type argument value into a metamodel closed type.
  • The declaration package contains all the declaration and open types.
  • The model package contains all the model and closed types.

Inspecting annotations

Constrained annotations can be inspected using the annotations function, like this:

// Does the process declaration have the Shared annotation?
value isShared = annotations(`Shared`, `value process`) exists;

or the related optionalAnnotation and sequencedAnnotations functions.

Note that annotations are queried for via their ceylon.language::Annotation type, not by the annotation constructor which was used to annotate the program element.

By: Gavin King, Stephane Epardaud
Subpackages
ceylon.language.meta.declaration

The Ceylon metamodel open type and declaration package.

ceylon.language.meta.model

The Ceylon metamodel closed type and model package.

Values
modulesSource Code
shared modules modules

Represents the list of Ceylon modules currently loaded at runtime.

Note that this contains all loaded modules, including those that are not imported by your module.

Since Ceylon supports module isolation at runtime, it is possible that there are more than one version of a given module loaded at the same time.

Usage example

Here's how you would iterate all the loaded modules and print their name and version:

import ceylon.language.meta { modules }

for(mod in modules.list){
    print("Module: ``mod.name``/``mod.version``");
}
Functions
annotationsSource Code
shared Values annotations<Value, Values, in ProgramElement>(Class<ConstrainedAnnotation<Value,Values,ProgramElement>,Nothing> annotationType, ProgramElement programElement)
given Value satisfies ConstrainedAnnotation<Value,Values,ProgramElement>
given ProgramElement satisfies Annotated

The annotations of the given type on the given program element. For example:

// Does the process declaration have the Shared annotation?
value isShared = annotations(`Shared`, `value process`) exists;

The annotations may be returned in any order.

optionalAnnotationSource Code
shared Value? optionalAnnotation<Value, in ProgramElement>(Class<OptionalAnnotation<Value,ProgramElement>,Nothing> annotationType, ProgramElement programElement)
given Value satisfies OptionalAnnotation<Value,ProgramElement>
given ProgramElement satisfies Annotated

The value of given optional annotation type on the given program element, or null if the program element was not annotated with that annotation type. For example:

// Does the process declaration have the Shared annotation?
value isShared = optionalAnnotation(`Shared`, `value process`) exists;
sequencedAnnotationsSource Code
shared Value[] sequencedAnnotations<Value, in ProgramElement>(Class<SequencedAnnotation<Value,ProgramElement>,Nothing> annotationType, ProgramElement programElement)
given Value satisfies SequencedAnnotation<Value,ProgramElement>
given ProgramElement satisfies Annotated

The values of given sequenced annotation type on the given program element, or empty if the program element was not annotated with that annotation type. For example:

// Does the sum declaration have any ThrownException annotations?
value throwsSomething = sequencedAnnotation(`ThrownException`, `function sum`) nonempty;

The annotations may be returned in any order.

typeSource Code
shared ClassModel<Type,Nothing> type<out Type>(Type instance)
given Type satisfies Anything

Returns the closed type and model of a given instance. Since only classes can be instantiated, this will always be a ClassModel model.

typeLiteralSource Code
shared Type<Type> typeLiteral<out Type>()
given Type satisfies Anything

Functional equivalent to type literals. Allows you to get a closed type instance for a given type argument.

For example:

assert(is Interface<List<Integer>> listOfIntegers = typeLiteral<List<Integer>>());
Classes
modulesSource Code
shared modules

Represents the list of Ceylon modules currently loaded at runtime.

Note that this contains all loaded modules, including those that are not imported by your module.

Since Ceylon supports module isolation at runtime, it is possible that there are more than one version of a given module loaded at the same time.

Usage example

Here's how you would iterate all the loaded modules and print their name and version:

import ceylon.language.meta { modules }

for(mod in modules.list){
    print("Module: ``mod.name``/``mod.version``");
}