Functions Application¶
Applying a function to an argument, so as to obtain the corresponding value from its range, is depicted by juxtaposing a reference to the function with its arguments encompassed in parentheses:
@f(1, 2, 3)
represents the applictaion of the function @f
to three
arguments 1
, 2
and 3
.
The reference to the function can be any expression returning a functional value, like a lambda-expression:
(\$x.($x+1))(3)
or an arbitrary expression like a conditionnal expression:
(@random() < 0.5 ? @f : @g)(3)
In this last application, half the time the function @f
is called and half the time the function @g
is called.
Arguments can also be simple expression:
@f($x, @g(2), $tab[3])
the value of the first argument in the call to @f
is the
value of the variable $x
, the value of the second argment
is the result of the application of @g
to the argument
2
and the value of the third argument is the value of the
fourth element of the tab refered by the variable $tab
.
Infix notation for function calls¶
A function call is usually written in prefix form:
@drop($t, 1)
@scramble($t)
It is possible to write function calls in infix form, as follows:
$t.@drop(1)
$t.@scramble()
The @
character is optional in the naming of a function in infix call,
so we can also write:
$t.drop(1)
$t.scramble()
This syntax is reminiscent of the function/method call in SuperCollider. The general form is:
arg₁ . @fct(arg₂, arg₃, ...) ; or more simply
arg₁ . fct(arg₂, arg₃, ...)
The argᵢ
are expressions. Notice that the infix call,
with or without the @
in the function name, is not ambiguous with the
notation exe.$x
used to refer to a local variable
$x
in a compound action from the exe
of
this action, because the name of a function cannot start with the $
character.
The infix notation is less general than the prefix notation, because in the prefix notation, the function can be given by an expression. For example, functions can be stored into an array and then called following the result of an expression:
$t := [@f, @g]
; ...
($t[$x])()
will call @f
or @g
following the value of
the variable $x
. This cannot be achieved with the infix
syntax: only function names (with or without @
) are accepted in the
infix notation, not expressions. In addition, a function without arguments
cannot be called in infix form.
The use of this notation will become apparent with the notion of method presented in chapter Actors.
Named parameters and out-of-order application¶
A user-defined function introduces parameter that are denoted by variable's identifiers in the paramter list's definition. For instance, in the definition:
@fun_def f($x, $y) { return $x + 2*$y }
the variables $x
and $y
denote the
function's parameters. The associated identifier can be used in the
application of a named function antescofo @f
to provide the
arguments out-of-order:
@f(y = 10, x = 2)
the previous call will return 22
. Notice that in a
function call, the name of the parameter is the corresponding variable's
identifier without the $
.
It is possible to mix named arguments and positional arguments in the same function call, provided positional arguments all appear before named arguments:
@f(2, y = 10) ; OK
@f(y = 10, 2) ; BAD
Named arguments are useful for functions that have a large number of parameters: the name is used as a mnemonic. This mechanism fit wells with the possibility to specify default values for arguments.
Nota Bene: named arguments can be used only for named functions. This feature does not work with lambda-expressions.
Optional arguments and default argument's value¶
The definition of a named function may specify a default value for some parameters. In this case, the parameter becomes optional in the function call: the default value is used in place of the missing argument. For instance:
@fun_def @f($x, $phase = 0) { return @sin($x + $phase) }
With these definition, the calls
@f(1, 0) @f(1) @f(x = 1) @f(x = 1, y = 0) @f(y = 0, x = 1)
In function definition, default argument's values must be provided after all non-optional arguments.
Nota Bene: default argument's value can be used only for named functions. This feature does not work with lambda-expressions.
Function Call Evaluation Strategy¶
The evaluation strategy is the mechanism that governs the evaluation of function application: when to evaluate the argument(s) of a function call and what kind of value to pass to the function.
Antescofo functions implement call-by-value strategy. Argument
evaluation order is not specified and is subject to change from one
implementation of the language to the other. And Antescofo functions are
strict: all arguments
are fully evaluated before evaluating the function body. So, logical
operators &&
, ||
are not functions, they
are specials forms (because they do not necessarily evaluate all
arguments).
The call-by-value evaluation strategy must be tempered by the fact that data-structures (tab, map, nim, string) are referred to through a pointer. See the side page Argument Passing Strategies.
Antescofo functions can be impure: they can have side effects through the in-place modification of a mutable data-structure or more simply, through the assignment of a global variable.
Tracing Function Calls¶
It is possible to trace the calls to a named function during the program
run with the two predefined functions: @Tracing and @UnTracing. The
trace is emitted on Max or PD console (or on the output specified by the
-–message
option for the standalone).
The two predefined functions admit a variety of arguments:
-
no argument: all user-defined functions are traced/untraced.
-
the functions to trace/untrace:
@Trace(@in_between,"@fib")
will trace the calls and the returns of the listed functions. Notice that the function to (un)trace can specified with their name or via a string. -
a tab that contains the functions to (un)trace through their name or through strings.
Here is an example:
@fun_def @fact($x) { if ($x < 1) { 1 } else { $x * @fact($x-1) } }
_ := @Tracing(@fact)
_ := @fact(4)
which generates the following trace:
+--> @fact($x=4)
| +--> @fact($x=3)
| | +--> @fact($x=2)
| | | +--> @fact($x=1)
| | | | +--> @fact($x=0)
| | | | +<-- 1
| | | +<-- 1
| | +<-- 2
| +<-- 6
+<-- 24
Curried Functions¶
In Antescofo, named or anonymous intentional functions can be partially applied. Partial function application says “if you fix the first arguments of the function, you get a function of the remaining arguments”. This notion is related to that of curried functions, introduced and developed by the mathematician Haskell Curry. The idea is seeing a function that takes n arguments, with n > 1, as equivalent to a function that takes only 1 argument and that returns a function that takes n - 1 arguments1.
Consider for instance
@fun_def @f($x, $y, $z) { $x + 2*$y + 3*$z }
This function takes 3 arguments, so
@f(1, 2, 3)
returns 14 computed as: 1 + 2*2 + 3*3. The idea of a curried
function, or partial application, is that one can provide less than three
arguments to the function @f
. For example
@f(11)
is a function still awaiting 2 arguments, y and z, to compute finally 11 + 2*y + 3*z. And function
@f(11, 22)
is a function still awaiting one argument, z, to compute finally 55 + 33 z.
Curryed lambda-expression¶
Using the notation provided by lambda-expression, we can say that
@f(11, 22)
is equivalent to:
\$z.(11 + 44 + 3*$z)
Notice that currying occurs for both named and anonymous function. For
instance, all previous examples can be achived by replacing
@f
by $f
provided that:
$f := \$x, $y, $z.( $x + 2*$y + 3*$z )
In other word,
\$x, $y, $z.( $x + 2*$y + 3*$z )
is implicitly equivalent to
\$x.( \$y.( \$z.($x + 2*$y + 3*$z) ) )
\$x, $y.( \$z.($x + 2*$y + 3*$z) )
\$x.( \$y, $z.($x + 2*$y + 3*$z) )
Partial application of lambda-expression and implicit currying may seems useless, because it is always possible to write concisely nested unary lambda-expression as showed in the previous example. However, a partial application results in the building of a lambda-expression and successive partial application uniformly build new lambda-expressions. In fact, named functions and lambda-expressions are managed uniformly in Antescofo: only the naming mechanism and the location where a definition is accepted, differ.
Curryed function and optional arguments¶
In a partial application, all optional arguments, if any, are considered as provided. For instance, with definitions
@fun_def @f($x, $y = 0, $z = 1) { $x + 2*$y + 3*$z }
; then
$h1 := @f() ; $h1(n) == n + 3
$h2 := @f(y = 22) ; $h2(n) == n + 47
$h2 := @f(z = 33) ; $h3(n) == n + 99
All three function $h1
, $h2
and
$h3
are unary functions because the value corresponding to
parameters x
and y
are either explicitly
provided or specified through their default value.
Application of partial application¶
Curried functions are extremely useful as arguments of higher-order
functions (i.e., functions taking other functions as arguments). An
example has been given in the definition of @fibonacci
to
provide a predicate to the case
.
For a more appealing example, consider the function @find(t, f)
that returns the first index i such thaf(i, t[i]) is
true. Suppose that we are looking for the first index whose associated
value is greater than a. The value a will change during the program
execution. Without relying on currying, one may write
@global $a
@fun_def @my_predicate($i, $v) { $v > $a }
...
$t := ... ; somme tab computation
$a := 3
$i := @find($t, @my_predicate)
But this approach is cumbersome: one has to introduce a new global
variable and must remember that the predicate works with a side effect
and that the global variable $a
must be set before using
@my_predicate
. Using partial application, the
corresponding program is much simpler and does not make use of an
additional global variable:
@fun_def @my_pred($a, $i, $v) { $v > $a }
...
$t := ... ; somme tab computation
$i := @find($t, @my_pred(3))
The expression @my_pred(3)
denotes a function awaiting
two arguments i and v to compute v > 3, which is exactly what
@find expects. However, the use of a lambda-expression is even simpler
for this toy example because the body of the function is a simple short
expression.
All user defined functions are implicitly curried and almost all predefined functions are curried. The exceptions are the special forms and overloaded predefined functions that take a flexible number of arguments, namely: @dump, @dumpvar, @flatten, @gnuplot, @is_prefix, @is_subsequence, @is_suffix, @normalize, @plot, @rplot, and @sort. When a predefined function does not support partial application, an error message is emitted when an incorrect application occurs.
-
Sometimes a subtle distinction is made between currying and partial function applications. A curried function is a function of arity 1 eventually returning a function which is also curried (expecting one argument). In contrast, partial function application refers to the process of fixing a number p of arguments to a function of arity n, producing another function of smaller arity n - p. ↩