Function and method declarations

A function accepts arguments and returns a value.

When a function is a member of a type is it called a method.

Usage

A trivial function declaration using a block looks like this:

void trivialBlock() {
    /* method block: statements */
}

Alternatively it's possible to declare a function using fat arrow, =>, like this:

void trivialSpecifier() => anotherMethod();

The general form of a function declaration looks like either of these:

ANNOTATIONS
TYPE exampleBlock
        <TYPE-PARAMETERS>
        PARAMETER-LISTS
        given TYPE-PARAMETER-CONSTRAINTS {
    FUNCTION-BODY
}
// or
ANNOTATIONS
TYPE exampleSpecifier
        <TYPE-PARAMETERS>
        PARAMETER-LISTS
        given TYPE-PARAMETER-CONSTRAINTS 
    => EXPRESSION;

Where:

Description

Method receiver

Method invocations have a receiver, an instance of the type that declares the method. Within the method body, the expression this refers to this receiving instance.

A top level function does not have a receiver.

Return type

A non-local function declaration always specifies the return type of the function, or the keyword void if the function has no specific return value.

The type system considers a void function identical to a function declared to return Anything. In particular, a void method may be refined by a subtype to return a more specific type. The value actually returned from an unrefined void function is always null:

class Top() {
    shared default void m() {}
}
class Sub() extends Top() {
    shared actual Boolean m() => true
}
void example() {
    Anything topM = Top().m(); // topM is null
    Boolean subM = Sub().m();
}

Type inference

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

class C() {
    function f() => 0; //inferred type Integer
}

If the local function doesn't return a value void is used instead of function.

Type parameters

A function declaration may have a list of type parameters enclosed in angle brackets (< and >) after the function name.

void generic<Foo, Bar>(){
    /* method block: statements 
       type parameter Foo and Bar are treated as a type */
}

Of course, methods may be members of types which themselves have type parameters:

class Generic<Foo>() {
    void method(Foo z) {
    }
}

A function declaration with type parameters may have a given clause for each declared type parameter to constrain the argument types.

Parameter list

A function declaration must have one or more parameter lists.

Most commonly just one parameter list is required, but higher order functions can use two or more:

String url(String host)(String path) => "http://``host````path``";
String(String) ceylonUrl = url("ceylon-lang.org");
String ceylonBlog = ceylonUrl("/blog);

Function blocks

The body of a function may be composed of statements in a brace-delimited block.

The body of a non-void function must definitely return a value using the return statement. The following code will be rejected by the compiler:

String fun(Boolean bool) {
    if (bool) {
        return "hello";
    }
    // error: missing return
}

Similarly a void function must only use the form of return which lacks an expression.

Function specifiers

A block with a return statement is unnecessarily verbose for a function that just evaluates a single expression and returns its result. In this case, we prefer to use the fat arrow (=>) syntax:

Integer addTen(Integer i) => i+10;

Note that you can use this to partially apply a function (or any other instance of Callable):

function zeroTo(Integer n) => Range(0, n);

Callable type

The callable type of a function expresses, in terms of the Callable interface, is the function's return type and parameter types. For example, the callable type of:

String stringExample(Integer i, Boolean b) => "";

is String(Integer, Boolean), and the callable type of:

void voidExample() {}

is Anything().

Exceptions

Ceylon doesn't have checked exceptions, so it's never necessary to declare what exceptions a method can throw.

The throws annotation may be used to document thrown exceptions.

Different kinds of method

formal and default methods

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

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

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

shared functions

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

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

Metamodel

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

See also