By Andrew J. Wozniewicz
Milwaukee, September 10, 2008
In WANTScript, modules are first-class values, that is, they can be passed as arguments, returned from function calls, bound to variable names, etc., just like simple types such as strings, Booleans, and numbers.
Blocks of code that can be passed as arguments are commonly (and often incorrectly) referred to as closures. The purpose of a closure is to simplify the process of passing a piece of functionality to a module, together with an evaluation environment containing bound variables.
By design, WANTScript does not support true closures. It allows the passing of existing named blocks (modules) as parameters to other modules, but it does not bind the environment of the passed named block. In that regard it is more similar to Pascal and C++, with their support for function pointers, than to Smalltalk, Ruby, or Scala, which support true closures.
Here is an example of passing a function as a parameter in WANTScript:
project FuncAsParam function Add(N1, N2)
return N1 + N2
function Mult(N1, N2)
return N1 * N2
function Max(N1, N2)
if N1 >= N2 then
function Min(N1, N2)
if N1 <= N2 then
Param1, Param2, ExpectedValue)
Result := Action(Param1,Param2)
" (expecting ", ExpectedValue, ")")
FuncAsParam defines a number of inner functions, each of which takes two numeric parameters. It also defines an
Evaluate procedure that takes an
Action parameter, as well as two numeric parameters
Param2, and a numeric
ExpectedValue parameter that is being passed in for display purposes.
Evaluate procedure treats its
Action parameter like a function that takes two arguments: it calls it with the arguments
Param2, the values of which it itself received from the caller.
The caller in this case is the main module
FuncAsParam, which calls the
Evaluate procedure repeatedly, each time with a different
Action. First, the
Action is the
Add function; then the
Mult function, the
Max function, and finally the
The output of this program is as follows:
12 (expecting 12)
35 (expecting 35)
7 (expecting 7)
5 (expecting 5)
You can see how different actions produce different results, even though the numeric arguments passed into
Evaluate are the same in each case.
The key to passing a module as a parameter is the module-reference operator denoted by the
@-sign (analogous to the
@, or the "address of" Pascal operator). If you were to list a function name, such as
Add, without the
@-sign as one of the parameters to Evaluate, it would be understood as a function call and the compiler would complain about missing parameters:
Evaluate(Add, 7,5, 12)
The first argument here is a reference to
Add, which is treated like a call to the function, and - since this call does not include any arguments - it is bound to fail. If you want to pass the function as a whole as an argument, you must use the module-reference operator
In this case, thanks to the
@-sign in front of the function name, instead of trying to invoke the specified function, the runtime is passing the entire function as a parameter for
Evaluate to call internally.