Passing Functions As Parameters

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 //test033

  function Add(N1, N2)
    return N1 + N2

  function Mult(N1, N2)
    return N1 * N2

  function Max(N1, N2)
    if N1 >= N2 then
      return N1
      return N2

  function Min(N1, N2)
    if N1 <= N2 then
      return N1
      return N2

  procedure Evaluate(Action,
    Param1, Param2, ExpectedValue)
    var Result
    Result := Action(Param1,Param2)
    WriteLn( PadLeft(Result,2),
      " (expecting ", ExpectedValue, ")")

  Evaluate(@Add,7,5, 12)
  Evaluate(@Mult,7,5, 35)
  Evaluate(@Max,7,5, 7)
  Evaluate(@Min,7,5, 5)


The project 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 Param1, and Param2, and a numeric ExpectedValue parameter that is being passed in for display purposes.

The Evaluate procedure treats its Action parameter like a function that takes two arguments: it calls it with the arguments Param1, and 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 Min function.

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) //Does NOT work!!!

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 @:

Evaluate(@Add,7,5, 12)

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.