# 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

```

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
```

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)
\$\$\$;
```