Functions are routines which can be called with arguments passed in and return some results. Also functions itself can be passed as parameters to another functions, returned from another functions as results, assigned to values etc. Jarog supports procedural paradigm as well as functional. Function starts with symbol $ and contains two section - header and statements:

    arg <arg1>: <type1>;
    arg <arg2>: <type2>;
): <result_type> do
    <statement 1>;
    <statement n>;

Header is enclosed into parenthesis and is used for defining parameters, local constants and variables or closures. Statements are executed when function is called. If function has only one statement, do and end can be omitted. If function has no instructions at all empty block do end is required.

Parameters and results

Function can accept one or more parameter and also return one result. Or do not accept any parameter or return any result. Parameters are defined with keyword arg, result type is defined after semicolon in the end of the header. Definition is similar to variables. All parameters are passed by value, there is no way to pass parameter by reference except two special types: functions and pipes. Also parameters and results can have initial values (except pipes), in case of parameter it will act like a default parameter value. Parameters are accessible inside function like regular variables. To return a result from a function a return statement have to be used. Function can have multiple return statements or do not have any (in this case function will return a default value).


const triangle := $(
    arg a: int := 5;
    arg b: int;
): float return (a ^ 2 + b ^ 2) ^ 1/2;

Now this function can be called as next:

log triangle(8, 4);        # regular call, all parameters are set
log triangle(a: 8, b: 4);  # parameter name is optional
log triangle(b: 4, a: 8);  # but can swap arguments

In case of using named parameters only some of them can be specified. In this case other parameters will be initialized with their initial values or with default initial values according to their type. Also function can be called without passing any arguments

log triangle(b: 4);        # pass only one argument, a is 5 in this case
log triangle(a: 8);        # notice that default value for b is not 
                           # specified but b is int so default value will 
                           # be 0
log triangle();            # all params are set to their default values

Let’s say we have next function. It accepts three parameters, a and b are integers and are implicitly initialized with 0, c is a string and is explicitly initialized with string “hello”.

const f := $(
    arg a, b: int;
    arg c: string = "hello";
): struct(int, int) do
    return {a, b};

Now we can call this function.

var r1, r2: int;    
{r1, r2} := f(1, 2, "test");  # func result will be assigned to variables
{r3, r4} := f(a: 1);          # only first parameter a is set, parameter
                              # b is initialized as 0 and c is "hello"

Functions as variables

In examples above functions were assigned to constants. But if there is a variable of a proper type, it also can contain a function.

const sqr := $(
    arg a: int;
): int return a ^ 2;

var s: func(n: int -> int) := sqr;

Or just like this:

var s: func(n: int -> int) := $(
    arg a: int;
): int return a ^ 2;

If we have defined variable of functional type but it doesn’t has initial value, it is initialized with a special empty function. This function accepts parameters defined in function type and return results also defined by function type, but results are initialized with their default values. See next example.

var f: func(a, b: int -> string);
var r: string := "test";
r := f(1, 2);        # no error here, r is empty string now

Variables make possible to reassign functions during program execution as well as passing them as function parameters. Also it is important to use parameter and result names from the function type and not from the function cause function type overrides parameter names outside of function scope. See example below, function s is initialized in example above. Function type describes parameter named n but function itself uses parameter a.

var n: int;
n := s(n: 5);        # correct, 5 will be assigned to a
n := s(a: 5);        # incorrect, there are no parameters named a in
                     # function type definition

Nested functions

Functions can be nested i.e.В declared inside other functions. Nested function are accessible only in their parent function like other variables. See [Scope visibility]. Only nested function can have [closures]. Also nested functions can be returned from a parent function.

A special case is when nested function with closure is retuned from a parent function. In this case a memory allocated for a closure will not be releases unless all function copies retuned from parent are released. All this functions will share the same closure value which will act like a global variable.

const make_f := $(
    var n: int := 0;
): func(a: int -> int) return $(
        arg a: int;
        use n;
    ): int do 
        n := n + a;
        return n;

f := make_f();
log f(1);           # returns 1
log f(2);           # returns 3

Constant functions

Functions are constants if they has no [closures]-variables. Constant function can be defined as constants. Functions with closures-variables can be defined only as variables.


In fact functions in Jarog don’t have names. All functions are anonymous by default and can not be called by name from inside. To avoid this issue a special variable self was added. This variable contains a function by itself so it can be called recursively.

const fib := $(
    arg n: int;
    use fib;                              # error! at this time 
                                          # constant fib is still not 
                                          # defined
): int do
    if (n < 2) 
        return 1
        return self(n - 1) + self(n - 2); # correct way to make recursive 
                                          # call