title logo

back contents forward

Chapter 4

functions

A function chunk has the form \ ( parameters )  body  end (in standard Lua you must use the word function in place of \ ). The parameters are a comma-separated list of identifiers, which are treated as local variables in the body.

An expression for a function is applied by following it by a comma-separated list of arguments in parentheses (though, as we have seen, for single arguments, in certain circumstances, these may be omitted). Every function application contains an implicit multiple assignment, as if the parameters were to the left of an equal sign and the arguments were to the right. In fact, in Lua assignments can be multiple.

             x,y,z = y,x
This switches the values of the variables x and y and assigns nil to z . In a multiple assignment the expressions on the right hand side are all evaluated first, before any of the assignments are made. If there are too many variables on the left hand side the extra ones are assigned a nil value. If there are not enough, the extra expressions on the right are ignored.

In Lua functions can return multiple arguments. Think of the symbol => (in standard Lua the word return ) as a sort of equal sign followed by the right hand side of an assignment.

  minmax = \(x,y)
            if x<y then => x,y else => y,x end -- if
           end -- function
  min,max = minmax(foo,bar) 
If an expression that returns multiple results is used in the right hand side of a multiple assignment, then, unless it is the last expression, only its first value is used.

         a,b,c = 100,minmax(12,7)
         print(a," ",b," ",c) --> 100 7 12
         a,b,c = minmax(12,7),100
         print(a," ",b," ",c) --> 7 100 nil

Enclosing an expression in parentheses ensures that only its first result is returned.

Many of the functions in Lua's standard libraries that are supposed to return a non-nil first result, signify that an error has occurred by returning nil as the first result with the error message as the second result .

error handling

When something goes wrong with the program, execution stops and an error message is output. You can make this happen using the built in function error . It takes a string as an argument, which it appends to the error message. This is called raising an exception. A useful built in function is assert . It could be defined as

     assert = \(x,mesg) => x or error(mesg or "") end
Sometimes you need to do some cleaning up before raising an error, such as closing a file or closing down a task. For this purpose RiscLua provides a function  newtry . This takes a cleanup function as an argument and returns a function that behaves like assert but which calls the cleanup function before raising an exception. If we write

         try = newtry(cleanup)
then try behaves like

          try = \(x,mesg)
                if x then => x end -- if
                cleanup()
                error(mesg or "")
                end -- function

RiscLua also provides a function protect . If f is a function that does not return a nil value then protect(f) behaves exactly as f does when no exceptions are raised, but returns a nil value followed by an error message when one is. When we later come to discuss how one calls software interrupts (SWIs) in RiscLua, we have to advise that even though the error-returning versions of SWIs are used, it is quite possible for errors in SWIs to cause a crash. Standard Lua, on the other hand, has been designed to be safe. Calling SWIs is an inherently unsafe business, unfortunately.

For type-checking

  the = \(s,x)
     local t,fmt = type(x),"Expected a %s but got a %s"
     => t == s and x or error(fmt:format(s,t))
     end -- function

is pretty. See its use below.
variable numbers of arguments

The symbol  ...  can be used as the last parameter in a function value to denote a variable number of parameters.

 printf = \(s,...) =>
      (the("string",s)):format(...) end -- function

Here is a function that returns the number of arguments it is supplied with.

  numvar = \(...) => select('#',...) end -- function
  print(numvar(10,20,30,40)) --> 4

If the first argument of select is a number, n, then it returns all the arguments from the n-th onward.
recursion

Basic programmers are used to applying functions first and having their definitions appear later in the program text. In Lua an identifier's definition must precede its use. This means that to understand a Lua program top-down you may have to read it backwards. However, function definitions can be recursive.

  fact1 = \(n)
          assert(n > 0, "Bad argument for factorial")
          if n == 0 then => 1 end -- if
          => n*fact1(n-1)
          end -- function

There is a slight complication that you must beware of when defining local functions recursively. Why does this not work?

  fact2 = \(n)
          assert(n > 0, "Bad argument for factorial")
          local f = \(n,a)
                    if n == 0 then => a end -- if
                    => f(n-1,n*a)
                    end -- function
          => f(n,1)
          end -- function 
The reason is that the second occurrence of f in the program is not in the scope of f . Remember that a local variable's scope does not begin until after its declaration as local. The correct program is

    fact2 = \(n)
            assert(n > 0, "Bad argument for factorial")
            local f
            f = \(n,a)
                 if n == 0 then => a end -- if
                 => f(n-1,n*a)
                end -- function
            => f(n,1)
            end -- function 
In fact fact2 is a better definition of the factorial function than fact1 . This is because  f is tail-recursive - the function f  returns an expression whose outermost level is an application of itself. The Lua interpreter can exploit this fact to compile more efficient code that uses a jump without having to save and restore state from the stack. This is a standard optimization known from the early days of programming language implementation.
the paradoxical combinator

As an exercise in distentangling scopes, here is an old chestnut from the 1930s when mathematicians first began to look into higher order functions.

     Y = \(f)
         local g = \(x) => x(x) end
         => g(\(x) => f(\(y)
                        local h = x(x)
                        => h(y)
                        end)
              end)
        end

This function has a remarkable property: the expression Y(f) evaluates to f(Y(f)) . In other words, it is a solution of the equation

         f(x) == x
or a fixed point of f . Note that there are two variables called x in different scopes. Here is a diagram showing how the scopes nest within each other. An expression ((a,b,...)) denotes a scope in which the variables a,b,... are local.

                          ((f,g))
                             |
              ---------------------------------
             |                                 |
           ((x))                             ((x))
                                               |
                                            ((y,h))

The function  fact1  above may be seen to be a fixed point of the function

   makefact = \ (f) => \ (n)
        assert(n > 0, "Bad argument for factorial")
        if n == 0 then => 1 end -- if
        => n*f(n-1)
                        end -- function
              end -- function

That is to say

    fact1 == makefact(fact1)
So we have a third, rather exotic, method of defining factorial as the function Y(makefact) . Note that there is no recursion used here.

back contents forward