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)
will all return the same value.

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.


  1. 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