next up previous contents index
Next: Events Up: Functions Previous: Missing Parameters

The Function Body

      The body of a Glish function has one of two forms:

expression

{ statementtex2html_wrap_inline15496 statementtex2html_wrap_inline15498 tex2html_wrap_inline15512 }

When a function using the first form is called, it evaluates expression and returns the result as the value of the function call. With the second form, the statements are collected in a statement block (see § 5 and § 5.9), and then the statements are executed sequentially. The value of the last statement executed is returned. Most statements   do not have a value associated with them. If the last executed statement is one of these, the function call returns F. If the last executed statement is an expression (see § 5.1, page gif) or a return statement (§ 5.6, page gif) then the call returns the value of the expression.

  Functions may call themselves either directly or indirectly; there is no limit on the depth of calling other than the available memory.

Scoping

 

  Glish supports three levels of scoping: function, global, and local.

function Scope

  By default, all variables which are assigned within a function are local to that function; they have function scope. So for example:

    function my_sin(y)
        { 
        x := sin(y)
        return x
        }
In this example, the x which is used, i.e. assigned to, in the function is local to this function. It will not modify a global x if one is defined. Note however that another Glish value is used in this function, sin. This is a global function, and since global variables are accessible in functions, the sin variable can be used. If sin were modified:
    function my_func(x)
        {
        sin := 2^x
        return sin
        }
then sin would be a variable which is local to this function, and the global sin would not be modified.  

global Scope

    A global variable persists throughout the execution of the Glish program, and sometimes functions must be able to modify these variables as well as access their values. For example, the following:

    x := 1
    function bump_x() { global x +:= 1 }
    bump_x()
    print x
will print the value 2. Here the global keyword is used to specify that a function variable should actually correspond to a global variable. If the global keyword had been left out, x would have been local to the function. When assignment is made to these function variables which have been tagged as global, the global variable of the same name is modified. global can also be used without the assignment:
    function bump_x_2()
        {
        global x
        x +:= 1
        }
bump_x_2 and bump_x are functionally equivalent. In either case, subsequent use of the variable within the function will actually operate on the global variable of the same name. The syntax of the global statement mirrors that of local (see § 5.9, page gif), but global's semantics are the inverse of local's.    

local Scope

    local scope was already introduced in § 5.9. It specifies that a particular variable is local to the current statement block. In this example:

    function trix(x)
        {
        y := x^2
        local z := x^3
        {
            local y := x^4, w
            w := 100
            print x,y,z,w
        }
        print x,y,z
        }
the local in front of the assignment of z is not needed since by default assigned variables in a function default to function scope, but it does no damage either. Here x, y, and z are local to the function block. Another y is introduced in the inner statement block along with w; these are local to the inner block. The invocation of trix(2) results in this output:
2 16 8 100
2 4 8

Variables of local and function scope usually ceases to exist once the statement block with which they are associated exits (but see § 6.6.2 for some exceptions). When the function is next called, the variable is recreated but with no trace of its former value. All function parameters are local to the function body.    

wider Scope

    While dividing variables into being either global variables which are accessible from anywhere or local variables which are only accessible within a give function is often sufficient, there are times when access is needed to variables which are defined in a wider scope but which are are still not global. Here is a simple example:

    function object(x)
        {
        value := x
        ret := [=]
        ret.get := func ( )  { return value }
        ret.set := func (nv) { wider value
                               value := nv }
        return ret
        }
  This example creates as simple function closure; the object function defines two functions which share a common variable. Without the wider statement, the set function would not be able to modified the shared variable, i.e. value.

The wider statement allows modification access to a variable defined in a wider scope. Without this statement, modification of a variable makes it local to the function by default.    

Persistent Local Variables

 

  There are two ways in which local variables can survive beyond the end of the function call that created them. Here ``survive" does not mean that subsequent calls to the function see the previous value, but that the value continues to exist after the initial function call returns.

The first way is by returning a reference to the variable. This used to be the preferred way of returning big values in Glish, but after copy-on-write (see § 6.4.3, page gif) was added to Glish, function writers now really do not need to worry about this issue. References, however, can still be returned (see § 3.8, page gif).

  The second way that local variables survive is if the function body or a statement block with local variables executes a whenever statement. The whenever statement specifies actions to be taken at a future time, asynchronously to the execution of the statements in the Glish program (see § 5.10.2, page gif, and particularly Chapter 7, page gif). For example, the following:  

    # Waits for x->foo, prints y
    # when it comes
    func announce_upon_foo(x, y)
        {
        whenever x->foo do
            print y
        }
    announce_upon_foo(x, 14)
    work()
    more_work()
    etc()
will print 14 whenever x generates a foo event. The value of y (which, being a parameter, is local to the function body) is remembered even after the call to announce_upon_foo returns. We could later add another call:
    announce_upon_foo(x, "hi there")
and when x generates foo events both 14 and "hi there" will be printed (in an indeterminate order).

When the function executes a whenever all of its local variables are preserved and can be accessed within the statements of the whenever's body. If those statements modify the variables then the modifications persist:

    func announce_upon_foo(x, y)
        {
        whenever x->foo do
            {
            print y
            y +:= 1
            }
        }
    announce_upon_foo(x, 14)
    announce_upon_foo(x, 7)
will print 14 and 7 upon x's first foo event, 15 and 8 upon the second, and so on.

Persistent local variables are particularly important for subsequences; see § 7.12, page gif.      


next up previous contents index
Next: Events Up: Functions Previous: Missing Parameters

Thu Nov 13 16:44:05 EST 1997