Scope visibility
All constants or variables can be global or local. Global variables or constants are defined in global scope. They are readable and writable from any function including nested and can be exported to other units. Local variables and constants are defined inside functions. They are inaccessible outside of their functions or inside nested functions.
const a := 5; # This is a global constant
const main := $(
arg args: list(string);
const b := 8; # local constant
var f: func() := $( # local variable and nested function
const c := a + b + 5; # error! b is inaccessible here but a is
# accessible
) do end;
) do end;
All types can be defined only globally.
Closures
Variables declared in parent function, can be pushed down to nested functions. This can be performed using keyword use
, multiple variables can be stated at once.
use <name1>, <name2>, <nameN>;
In fact nested function will have a copy of variable. Pushing down a variable makes function non-constant because variable value can not be defined during compilation. Pushing down constants doesn’t make function change it’s result in runtime so these functions still can be declared as constants.
const a := 5; # This is a global constant
const main := $(
arg args: list(string);
use a; # error! global functions cannot
# have closures;
var n: string := "Hello ";
const f := $(
use n; # this will throw error as function
# with variable closures can not
# not constant
) do end;
var f: func() := $(
use n; # this is ok
) do end;
) do end;
Only nested functions can have closures. Global values and variables can not be pushed as closures so functions defined globally cannot have closures. Closures are writable and changing closure inside function also will change it outside.
const main := $(
arg args: list(string);
var n: string := "Hello ";
var f: func(r: string);
) do
f := $(
in r: string;
use n; # now we have local variable n
) print(n + r);
f("Bill"); # Hello Bill
n := "Hi ";
f("Bill"); # now is Hi Bill,
end;
Yet one more example to explain how closures work
var f: func(a: int -> int) := $(
arg a: int;
): int return a ^ 2;
f := $(
in a: int;
use f;
): int return f(a); # previous value of f will be used so this will
# not be a recursive call
...
f(2); # 4 is returned
Currying
Jarog like other functional languages supports currying. Here is an example:
type _hellofunc: func(name: string);
const makeHello := $(
in g: string;
out f: _hellofunc;
) do
f := $(
in name: string;
use g;
) print(g + name);
end;
const main := $(
in args: list(string);
var hello: _hellofunc;
) do
hello := makeHello("Hello ");
hello("Bill"); # Hello Bill
hello := makeHello("Hi ");
hello("Bill"); # Hi Bill
end;
Name shielding
If local variable or constant have the same name as any global it will shield global variable.
const a := 5;
const main := $(
in args: list(string));
var a: int := 8;
) do
io::put(str::fmt("%d", [a])); # 8
end;
Shielding is not working for statement variables, instead statement variables should not conflict with local variables. See statements
Statement variables
Statement with
allows to declare a variable with visibility only inside it’s statement block. In fact this is a syntax sugar and is an equivalent to declaring a variable and assigning expression value to it but the difference is this variable is invisible outside with
block.
The variable is declared in local function scope. That means it can not shield any other variable in local function scope and will conflict with them.
Similar functionality also have each
and try-catch
statements.