| |
Navigation |
Synopsis Declare a function.
Syntax
Body is one of:
Modifiers may be:
Description
Variant 1A function declaration introduces a new function with nameName , typed formal parameters Type_{1} Var_{1} , a return type Type
and a Statement that forms the function body.
The type of Statement should be equal to Type .
The formal parameters may be used in Statement and get their value when function Name is invoked.
Variant 2A function may have a variable list of arguments, this has as syntax variant 2 given above.The last parameter of a function may be followed by ... and this has as effect that all remaining actual parameters
that occur in a call to this function are collected as list value of the last formal parameter.
Inside the function body, the type of this parameter will therefore be list[Type_{0}] .
Variant 3 and 4All formal parameter of a function can be Patterns. There are some restrictions however:
Body types
Parameterized types in function declarationThe types that occur in function declarations may also contain TypeParameters. In this way functions can be defined for arbitrary types. The type variable is bound (statically) at by the types of the parameters given at location of the call. The result type must be used at least once in any of the parameters.OverloadingFunction definitions may be overloaded, i.e. a function with the same name may be defined twice and a function may redefine a constructor of an AlgebraicDataType or a SyntaxDefinition.There are some restrictions however:
ModifiersTheModifiers affect visibility and special behaviour of functions:
Examples Declare a function
rascal>rel[int, int] invert(rel[int,int] R){ >>>>>>> return {<Y, X> | <int X, int Y> <- R }; >>>>>>>} rel[int,int] (rel[int,int]): rel[int,int] invert(rel[int,int]);Call it rascal>invert({<1,10>, <2,20>});
rel[int,int]: {
<10,1>,
<20,2>
}
In the following example we illustrate the use of type variables in function declarations.
Declare an inversion function that is applicable to any binary relation:
rascal>rel[&T2, &T1] invert2(rel[&T1,&T2] R){ >>>>>>> return {<Y, X> | <&T1 X, &T2 Y> <- R }; >>>>>>>} rel[&T2,&T1] (rel[&T1,&T2]): rel[&T2,&T1] invert2(rel[&T1,&T2]);Now apply it to relations with different types: rascal>invert2({<1,10>, <2,20>}); rel[int,int]: { <10,1>, <20,2> } rascal>invert2({<"mon", 1>, <"tue", 2>}); rel[int,str]: { <1,"mon">, <2,"tue"> }As another example declare a function that can be used to swap the elements of pairs of arbitrary types (also see Tuple/Subscription): rascal>tuple[&T2, &T1] swap(tuple[&T1, &T2] TP) { return <TP[1], TP[0]>;} tuple[&T2,&T1] (tuple[&T1,&T2]): tuple[&T2,&T1] swap(tuple[&T1,&T2]); rascal>swap(<1, 2>); tuple[int,int]: <2,1> rascal>swap(<"wed", 3>); tuple[int,str]: <3,"wed">Here we use an overloaded definition with incomparable patterns: rascal>int f(int i) = 1; int (int): int f(int); rascal>int f(real r) = 2; int (real): int f(real); rascal>f(0); int: 1 rascal>f(0.0); int: 2And we may use default , as in:
rascal>int f(0) = 1; int (int): int f(int); rascal>default int f(int n) = n * f(n - 1); int (int): int f(int); rascal>f(0); int: 1 rascal>f(2); int: 2In combination with an AlgebraicDataType, which defines default functions implicitly for every alternative, we can define canonicalization functions. The same holds for SyntaxDefinitions, see Actions.
This definition implies a default function for t(), f() and neg(B):
rascal>data B = t() | f() | neg(B);
ok
the following definition will remove any nested neg before it is even constructed:
rascal>B neg(neg(B b)) = b; B (B): B neg(B); rascal>neg(t()); B: neg(t()) rascal>neg(neg(f())); B: f() |