Two syntaxes were added for binding arguments: ?() and function(). This gives Cadre a more natural function definition syntax. However, ?() is also very useful in some new control structures:
First, an example of the new function definition code:
[t]y = function(x) 1/x;
y(0.5); // returns 2[/t]
This supplements the previous syntax:
[t]y = function 1/_args[0];
y(0.5); // returns 2[/t]
Parameters can also be captured using the ?() primitive, which does the same thing and can appear at the start of a line:
[t]y = function ?(x) 1/x;
y(0.5); // returns 2[/t]
This is more useful in contexts where the "function" keyword is implicit, such as [t]each[/t], [t]sieve[/t], and [t]reduce[/t].
Four new control structures were implemented: [t]map[/t], [t]filter[/t], [t]fold[/t], and [t]for[/t]. [t]for[/t] is standard C, except with the peculiarity that commas are used to separate the arguments, not semicolons:
[t]var j = 0;
for(var i = 0, i < 10, i++) j += i;[/t]
[t]map[/t], [t]filter[/t], and [t]fold[/t] work like their Haskell equivalents—they take a function as their first argument, and take a list as their second (although map takes an arbitrary number of lists), and apply that function to the list, producing various results:
function | block version | purpose
|
---|
map(f, a, ...) | each(a, ...) ?(x, ...) f(x, ...); | Returns an array of the same length with all elements modified by f.
|
filter(f, a) | sieve(a) ?(x) f(x); | Returns an array of the same or shorter length, only keeping elements if f is true for them.
|
fold(f, a) | reduce(a) ?(x, y) f(x, y); | Returns a single value created by applying the same operation to the list elements until the list is exhausted. The first element of the list is assumed to be the base case. |
All of these have an [t]x[/t] version (e.g. [t]mapx[/t]) which retains the indices of the input array (in mapx/eachx's case, only of the first sequence). The map family of functions will fail if not all lists are the same length. If you ask for additional parameters, you'll start getting the indices—e.g. [t]map(f, l1, l2)[/t] provides four parameters to [t]f[/t] in total (data1, data2, index1, index2).
Note that since, in all cases (even the block cases), a stack frame is created, [t]return[/t] cannot be used from within these structures to break out of the parent function. This is a problem also faced by Ruby in certain circumstances. In Octavia, it is solved with the [t]answer[/t] keyword, which is essentially a return-then-break. To maintain good style it should not be used outside of one of these block statements.