Defining your own
It is possible to use locally user-defined functions for repetitive tasks. Such functions can be defined locally in the script or in customization, to allow shared usage of functions by all the system users.
Functions can be also redefined and undefined. Because the language construction is function based, core operations like IF
, WHILE
, even DEFUN
are also functions. If the factory-provided language function are undefined or redefined, the language might become locally (within single script execution) unusable.
Functions access global variables by default. Watch out when using common variable names such as i
for iterating to avoid clashing with other functions doing the same. Using LOCALS
or WITH
is highly recommended to avoid common pitfalls.
DEFUN - defining functions
DEFUN
has a simple syntax
DEFUN( <new_function_name>, <argument_name_list>, <function_source_code> )
Function source code is a regular ASTER script, with operations separated by semicolons ;
.
The return value of the function is the last value evaluated in the function body, similar to how it works for the complete scripts.
Examples
DEFUN("ADD_ONE",("A"),A+1); # Define function ADD_ONE
ADD_ONE(1) # Returns 2
DEFUN("MY_POWER",("X","Y"),POWER(X,Y)); # Simple example for a new POWER function
POWER(2,2) == MY_POWER(2,2)
LAMBDA - inline functions
Sometimes it’s not needed to define a named function, which is when inline functions come useful. ASTER allows defining inline functions and passing them around using variables. In technical terms, functions are first-class citizens in the language.
LAMBDA( <argument_name_list>, <function_source_code> )
Function source code is a regular ASTER script, with operations separated by semicolons ;
.
The return value of the function is the last value evaluated in the function body, similar to how it works for the complete scripts. Lambda call returns a function object reference that can be used as argument or passed as a function where it’s needed (e.g. in sorting).
Examples
# sorting
i=0;
while( i<100,
i=i+1;list[i]=ROUND(RANDOM(1000,9000),3)); # creating a list of 100 random numbers
sorted = qsort(list, lambda(("a","b"), A<B)) # sorted contains a sorted list now
# map operation
fsquare = lambda("x",x*x);
defun("map",("list","fun"),
with(i=0;r=0;c=count(list),
unset(r);
while(i<c,
i=i+1;
r[i] = {fun}(list[i])); # note the {} notation to call
r)); # variable named function
a=(1,2,3,4);
b=map(a,fsquare);
b[1] & " " & b[2] & " " & b[3] & " " & b[4] # Result: 1.0 4.0 9.0 16.0
Variable scoping
All the variables defined in the argument list name are automatically shadowing variables of the same name in the script. In other words, function arguments are locally scoped. Global variables of the same name are not accessible within the function body.
Example
A=5;
DEFUN("AP",("A"),A+1); # Define function AP, the global variable A is not accesible
AP(1); # Returns 2, as the global value of A was shadowed
A==5 # TRUE - Global variable is not changed
More complex variable scoping scenarios
In case the newly defined function is complex and needs more variables for implementation, it makes sense to define these variables also as locals, specifically for complex calculation sequences.
Function LOCALS
handles this scenario. It may be used also outside function definition, if needed for clarity or convenience. Nesting is also possible.
LOCALS( <variable_name_list>, <expressions> )
Examples
A=5;C=10;
D = LOCALS(("A","B"), # existing A and B will be shadowed
A=10;
A=A*A;
B=A/2;
C=B); # Accesses global C !
"A=" & A & " B=" & B & " C=" & C & " D=" & D # A=5 B= C=50 D=50
Convenient solution for scoping and assignments
LOCALS
being a basic ASTER construct might be useful for complex variable scoping scenarios. For more usual cases, where assignments are usually part of the declaration, using WITH
might be much more convenient. It analyzes the assignment code automatically shadowing named variables, so that the code can be leaner and less prone to errors as shadowed variables must be mentioned only once.
WITH( <assignment_code>, <expressions> )
Example
a=10; # global A
b=20; # global B
c=with(a=5;b=10,a+b); # shadows A and B, before executing assignment
a & " " & b & " " & c # returns 10 20 15
# 15 being sum of 5+10, original values of A and B protected automatically by shadowing
Calling other functions, including recurrence
It is possible to call other functions, or even the newly defined function itself, within the function body. It is also allowed to call DEFUN
from within another function, making it possible to create macros - a code that is writing new code.
Recurrent function calling is a common use case. Below example showcases using recurrence (specifically in this case called recursion) to calculate factorial of a number.
The formula for factorial is:

And its recursive definition:

Example
DEFUN("S","X",IF(X==1,1,S(X-1)*X)); # S is our new factorial function
S(5) == (5*4*3*2*1) # TRUE - 5 * 4 * 3 * 2 * 1 = 120