Functions

To define a function:

foo(x: int): bool := if (x > 10) then true else false

Usually the return type is optional and can be inferred, e.g. the following function returns a boolean type:

foo(x: int) := if (x > 10) then true else false

If the function is recursive the return type must be specified as in:

fact(x: int) := if (x = 1) then x else x * fact(x - 1) // Type error!

fact(x: int): int := if (x = 1) then x else x * fact(x - 1) // OK!

Declaration order

For convenience, functions can be used before their declaration. However, such declarations must be available in the scope and not in inner scopes.

The following is valid (note that g uses f before f is declared):

g(i: int) := f(i) * 2;
k := 1;
f(i: int) := i + k;

g(1)  // Result is 4

The following is invalid because the function f is only defined in an inner scope, so it isn’t visible to g.

g(i: int) := f(i) * 2;
k := 1;
{
    f(i: int) := i + k;
    g(1) // Type error!
}

Anonymous (lambda) functions

To define anonymous functions:

\(x: int) -> if (x > 10) then true else false

If functions have multiple arguments:

\(x: int, y: string) -> if (x > 10) then y else "smaller"

To specify the return type of an anonymous function:

\(x: int): string -> cast((x + 1) as string)

Extending or overriding infix binary operators

Attention

Support for infix operators is currently not implemented.

To define infix binary operators use the form:

infix +(a: int, b: string) := cast(a as string) + " " + b

You may also create new infix operators as in:

infix addtwo(a: int, b: int) := a + b + 2

1 addtwo 2 // 5

The priority of infix operators that extend or override built-in operators, such as +, -, *, /, %, <, … is the same as that of the original operators. Other operators that do not match any built-in operators have a lower priority compared to the built-in operators. Note that = and ==, as well as <> and != are de-sugared at the parser level, so overriding one form automatically overrides the other form as well.

Default arguments

Function definitions can include default arguments but these cannot be followed by non-default arguments, i.e. they must appear at the end.The following is valid:

inc(n: int, step: int = 1) := a + b;
inc(1) // result is 2
inc(1, 2) // result is 3
inc(1, step=1) // result is 2

The following definition is invalid:

inc(n: int, step: int = 1, v: int) // Error since v is a non-default argument following a default argument

Overloaded functions

Functions can be overloaded as long as their types are unambiguous distinct.

For example:

my_sum(x: int, y: int) := x + y
my_sum(x: string, y: string) := x + y

Calling functions

RAW is a functional language, i.e. functions are primary-class citizens in the type system. This means that functions can be stored in collections, passed as arguments, and can be nullable.If a function has a null value, then it cannot be executed. The result of calling it is null, e.g:

a: function(int, int) := null;
a(1) // null

If one of the arguments of the function is defined as not nullable but caller is passing a nullable value, then the function cannot be executed. In this case, the result of calling it is also null, e.g.:

inc(v: int not null, step: int not null) := v + step;
inc(1, 2) // result is 3
inc(null, 2) // result is null

Python Functions

It is possible to create python functions and mix them with RAW’s query language.

Declaration

Syntax:

<function name> := \python([<arg name> : <arg type>, ...]): <return type> -> $$$
    {python code}
$$$

For example:

foo := \python(x: int): bool -> $$$
    if x > 10:
        return True
    else:
        return False
$$$;

foo(11) // result is true

Note

In python functions definitions, the return type cannot be inferred and is mandatory.

Using Numpy Arrays

Python Numpy arrays are converted to mdarray type and vise versa.

For example:

foo := \python(): mdarray(double, x) -> $$$
    import numpy as np
    return np.array([1.1, 1.2])
$$$;

foo() // returns 1D packed_array

// the opposite is also true
bar := \python(x: mdarray(int, x)): bool-> $$$
    import numpy as np
    return isinstance(x, np.ndarray)
$$$;

a := packed_array x(0:9) with (x+1);
bar(a) // returns true

Attention

Only mdarrays of numbers, boolean or strings are supported in conversion to Numpy arrays.

Using SciPy and Sklearn

The popular data analysis modules: SciPy and Sklearn are also installed in RAW’s server and are available to use inside python functions.

For example:

predict := \python(train_x: collection(collection(double)), train_y: collection(double), test: collection(collection(double))): mdarray(double, x), -> $$$
    import sklearn
    import sklearn.linear_model
    regr = sklearn.linear_model.LinearRegression()
    regr.fit(train_x, train_y)

    return regr.predict(test)
$$$;