Product Documentation

Functions

Aug 31, 2016

Functions are a basic building block of programming -- they are a convenient and powerful way to group statements that perform a task. They are also the interface between NetScaler policy expressions and extension functions -- you define extension functions that are called by policy expressions. Functions consist of function definitions that specify what values are passed into and out of the function and what statements are executed for the function, and function calls, which execute functions with specific input data and get results from the function.

NetScaler Policy Extension Function Definition

Since the NetScsaler policy expression language is strongly typed, the definition of an extension function must specify the types of its inputs and its return value. The Lua function definition has been extended to include these types:

function self-type:function-name(parameter1: parameter1-type, etc.): return-type
     statements
end

where,

the types are NSTEXT, NSNUM, NSBOOL, or NSDOUBLE.

self-type is the type of the implicit self parameter that is passed into the function. When the extension function is used in an NetScaler policy expression, this is the value generated by the expression to the left of the function. Another way to view this is that the function extends that type in the NetScaler policy language.

The parameter-types are the types of each parameter specified in the extension function call in the policy expression. An extension function can have zero or more parameters.

return-type is the type of the value returned by the extension function call. It will be the input to the part of the policy expression, if any, to the right of the function, or else is the value of the expression result.

Example:

function NSTEXT:COMBINE_HEADERS() : NSTEXT

Use of the extension function in a policy expresssion:

HTTP.REQ.FULL_HEADER.AFTER_STR("HTTP/1.1\r\n").COMBINE_HEADERS()

Here the self parameter is the result of HTTP.REQ.FULL_HEADER.AFTER_STR("HTTP/1.1\r\n"), which is a text value. The result of the COMBINE_HEADERS() call is text, and since there is nothing to the right of this call, the result of the entire expression is text.

Local Function Definition

Besides extension functions, no global functions can be defined in an extension file. But local functions can be defined within extension functions using the normal Lua function statement. This declares the name of the function and the names of its parameters (also known as arguments), and like all declarations in Lua, does not specify any types. The syntax for this is:

local function function-name(parameter1-name, parameter2-name, etc.)
     statements
end

The function and parameter names are all identifiers. (The function name is actually a variable and the function statement is shorthand for local function-name = function(parameter1, etc.), but you don't have to understand this subtlety to use functions.)

Note that etc. is used here for continuation of the pattern of parameter names instead of the usual ... . This is because ... itself actually means a variable parameter list, which will not be discussed here.

Function Body and Return

The block of statements between the function and end statements is the function body. In the function body, the function parameters act like local variables, with values supplied by the function calls, as described previously.

The return statement supplies values to be returned to the caller of the function. It must appear at the end of a block (in a function, if then, for loop, and so on; It can be in its own block do return ... end). It may specify no, one, or more than one return values:

return -- returns nil
return expression -- one return value
return expression1, expression2, ... -- multiple return values

Examples:

local function fsum(a)
     local sum = 0
     for i = 1, #a do
          sum = sum + a[i]
     end
     return sum
end

local function fsum_and_average(a)
     local sum = 0
     for i = 1, #a do
          sum = sum + a[i]
     end
     return sum, sum/#a
end

Function Calls

A function call executes the body of a function, supplying values for its parameters, and receiving results. The syntax for a function call is function-name(expression1, expression2, etc.), where the function parameters are set to the corresponding expressions. The number of expressions and parameters need not be the same. If there are fewer expressions than parameters, the remaining parameters are set to nil. So you can make one or more parameters at the end of the call optional, and your function can check if they are specified by checking if they are not nil. A common way to do this is with the or operation:

function f(p1, p2) -- p2 is optional
     p2 = p2 or 0 -- if p2 is nil, set to a default of 0
     . . .
end

If there are more expressions than parameters, the remaining expression values are ignored.

As noted previously, functions can return multiple values. These returns can be used in a multiple assignment statement. Example:

local my_array = {1, 2, 3, 4}
local my_sum, my_ave = sum_and_average(my_array)

Iterator Functions and Generic For Loops

Now that we have introduced functions, we can talk about generic for loops. The syntax for the generic for loop (with one variable) is:

for variable in iterator(parameter1, parameter2, etc.) do
     statements in the for loop body
end

where iterator() is a function with zero or more parameters that provides a value for variable on each iteration of the loop body. The iterator function keeps track of where it is in the iteration using a technique called closure, which you don't have to worrry about here. It signals the end of the iteration by returning nil. Iterator functions can return more than one value, for use in a multiple assignment.

Writing an iterator function is beyond the scope of this paper, but there are a number of useful built-in iterators that illustrate the concept. One is the pairs() iterator, which iterates through the entries in a table and returns two values, the key and the value of the next entry.

Example:

local t = {k1 = "v1", k2 = "v2", k3 = "v3"}
local a = {} -- array to accumulate key-value pairs
local n = 0 -- number of key-value pairs
for key, value in pairs(t) do
     n = n + 1
     a[n] = key .. " = " .. value -- add key-value pair to the array
end
local s = table.concat(a, "; ") -- concatenate all key-value pairs into one string

Another useful iterator is the string.gmatch() function, which will be used in the following COMBINE_HEADERS() example.