Sather - A Language Manual - Closures

[back] [Abstract] [Copyright Notice] [Contents] [next]

# Sather - A Language Manual - Chapter 9
Closures

Routine and iter closures are similar to the 'function pointer' and 'closure' constructs of other languages. They bind a reference to a method together with zero or more argument values (possibly including `self`). The type of a closure begins with the keywords `ROUT` or `ITER` and followed by the modes and types of the underscore arguments, if any, enclosed in braces (e.g. '`ROUT{A, out B, inout C}`', `'ITER{once A, out B, C}'`). These are followed by a colon and the return type, if there is one (e.g. '`ROUT{INT}:INT`', `'ITER{once INT}:FLT'`).

## 9.1 Creating and Calling Closures

### 9.1.1 Creating a closure

A closure is created by an expression that binds a routine or an iterator, along with some of its arguments. The outer part of the expression is '`bind(...)`'. This surrounds a routine or iterator call in which any of the arguments or `self` may have been replaced by the underscore character '`_`'. Such unspecified arguments are unbound. Unbound arguments are specified when the closure is eventually called.

    a:ROUT{INT}:INT := bind(3.plus(_))
    b:ITER:INT := bind(3.times!);

Out and inout arguments must be specified in the closure type. If the routine has `inout` or `out` arguments

    swap(inout x, inout y:INT) is
       tmp ::= x;
       x := y;
       y := tmp;
    end;

as show below, they are mentioned in the type of the closure:

The routine `'swap'` swaps the values of the two arguments, `'x'` and `'y'`. '`r'` is a closure for binding the `'swap'` routine.

    r:ROUT{inout INT, inout INT} := bind(swap(_,_));

### 9.1.2 Calling a closure

Each routine closure defines a routine named '`call`' and each iterator closure defines an iterator named `'call!'`. These have argument and return types that correspond to the closure type specifiers. Invocations of these features behave like a call on the original routine or iterator with the arguments specified by a combination of the bound values and those provided to `call` or `call!`. The arguments to `call` and `call!` match the underscores positionally from left to right .

The previously defined closures are invoked as shown

    #OUT + a.call(4);  -- Prints out 7, where a is bind(3.plus(_)
    sum:INT := 0;
    loop
       sum := sum + b.call!;
    end;
    #OUT + sum;        -- Prints out 3 (0+1+2)

In the following example, we define a bound routine that takes an `INT` as an argument and returns an `INT`.

    br:ROUT{INT}:INT := bind(1.plus(_));
    #OUT + br.call(9);      -- Prints out '10'

The variable br is typed as a bound routine which takes an integer as argument and returns an integer. The routine 1.plus, which is of the appropriate type, is then assigned to br. The routine associated with br may then be invoked by the built in function `call`. Just as we would when calling the routine `INT::plus(INT)`, we must supply the integer argument to the bound routine.

### 9.1.3 Binding overloaded routines

When binding a routine which is overloaded, there might be some ambiguity about which routine is meant to be bound

    class FLT is
       plus(f:FLT):FLT  -- add self and 'i' and return the result
       plus(i:INT):FLT; -- add self and 'f' (after converting 'i' to FLT)
    end;

When binding the `plus` routine, it might not be obvious which routine is intended

    b ::= bind(_.plus(_));

In case of ambiguity, the right method must be determined by the context in which the binding takes place.

Binding in an assignment

If there is ambiguity about which method is to be bound, the type of the variable must be explicitly specified

    b:ROUT{FLT,FLT}:FLT := bind(_.plus(_)); -- Selects the first 'plus'

Binding in a call

A method may also be bound at the time a call is made. The type of the closure is determined by the type of the argument in the call.

    reduce(a:ARRAY{FLT}, br:ROUT{FLT,FLT}:FLT):FLT is
       res:FLT := 0.0;
       loop
          el:FLT := a.elt!;
          res := br.call(res,el);
       end;
       return res;
    end;

We can call the reduction function as follows:

    a:ARRAY{FLT} := |1.0,7.0,3.0|;
    #OUT + reduce(a,bind(_.plus(_)));
    -- Prints '11.0', the sum of the elements of 'a'

The second argument to the function `reduce` expects a `ROUT{FLT,FLT}:FLT` and this type was used to select which `plus` routine should be bound. When there could be doubt about which routine is actually being bound, it is very good practice to specify the type explicitly

    r:ROUT{FLT,FLT}:FLT := bind(_.plus(_));
    #OUT + reduce(a,r);

### 9.1.4 Points to note

- `out` and `inout` arguments must be left unbound. This is a reasonable restriction, since such arguments must return a value to the calling context. If such an argument were bound, when the closure is invoked, variables that existed at the point of closure binding would be affected. Such variables might not even be alive at the point where the closure is actually invoked.

### 9.1.5 Binding some arguments

When a routine closure is created, it can preset some of the values of the arguments.

    class MAIN is
       foo(a:INT, b:INT):INT is
          return(a+b+10);
       end;

       main is
          br1:ROUT{INT,INT}:INT := bind(foo(_,_));
          br2:ROUT{INT}:INT := bind(foo(10,_));
          #OUT + br1.call(4,3) + "," + br2.call(9);
                -- Should print 17 and  29
       end;
    end;

In the example above, `br2` binds the first argument of `foo` to 10 and the second argument is left unbound. This second argument will have to be supplied by the caller of the bound routine. `br1` binds neither argument and hence when it is called, it must supply both arguments.

Here we double every element of an array by applying a routine closure `r` to each element of an array.

    r :ROUT{INT}:INT := bind(2.times(_));
    loop
       a.set!(r.call(a.elt!))
    end

### 9.1.6 Leaving self unbound

bound routines are often used to apply a function to arbitrary objects of a particular class. For this usage, we need the self argument to be unbound. This illustrates how self may be left unbound. The type of self must be inferred from the type context (`ROUT{INT}`).

    r:ROUT{INT} := bind(_.plus(3));
    #OUT + r.call(5);                        -- prints '8'

In the following example we will make use of the `plus` routine from the `INT` class.

    ... from the INT class
    plus(arg:INT):INT is
    ... definition of plus

    main is
       plusbr1:ROUT{INT,INT}:INT := bind(_.plus(_)); -- self and arg unbound
       br1res:INT := plusbr1.call(9,10);             -- Returns 19
       plusbr2:ROUT{INT}:INT := bind(3.plus(_));     -- Binding self only
       br2res:INT := plusbr2.call(15);               -- Returns 18
       plusbr3:ROUT{INT}:INT := bind(_.plus(9));     -- Binding arg only
       br3res:INT := plusbr3.call(11);               -- Returns 20
       #OUT + br1res + "," + br2res + "," + br3res;
                  -- 19,18,20
    end;

In the above example, `plusbr1` leaves both self and the argument to `plus` unbound. Note that we must specify the type of self when creating the bound routine, otherwise the compiler cannot know which class the routine belongs to (the type could also be an abstract type that defines that feature in its interface). `plusbr2` binds self to `3`, so that the only argument that need be supplied at call time is the argument to the plus. `plusbr3` binds the argument of `plus` to `15`, so that the only argument that need be supplied at call time is self for the routine.

## 9.2 Further Examples of Closures

Just as is the case with C function pointers, there will be programmers who find closures indispensible and others who will hardly ever touch them. Since Sather's closures are strongly typed, much of the insecurity associated with function pointers in C disappears.

### 9.2.1 Closures for Applicative Programming

Closures are useful when you want to write Lisp-like "apply" routines in a class which contains other data . Routines that use routine closures in this way may be found in the class ARRAY{T}. Some examples of which are shown below.

    every(test:ROUT{T}:BOOL):BOOL is
       -- True if every element of self satisfies 'test'.
       loop
          e ::= elt!;   -- Iterate through the array elements
          if ~test.call(e) then
             return false;
          end
          -- If e fails the test, return false immediately
       end;
       return true
    end;

The following routine which takes a routine closure as an argument and uses it to select an element from a list

    select(e:ARRAY{INT}, r:ROUT{INT}:BOOL):INT is
       -- Return the index of the first element in the array 'e' that
       -- satisfies the predicate 'r'.
       -- Return -1 if no element of 'e' satisfies the predicate.
       loop i:INT := e.ind!;
          if r.call(e[i]) then
             return i;
          end;
       end;
       return -1;
    end;

The selection routine may be used as shown below:

    a:ARRAY{INT} := |1,2,3,7|;
    br:ROUT{INT}:BOOL := bind(_.is_eq(3));
    #OUT + select(a,br);  -- Prints the index of the first element of 'a'
                          -- that is equal to '3'. The index printed is '2'

### 9.2.2 Menu Structures

Another common use of function pointers is in the construction of an abstraction for a set of choices. The `MENU` class shown below maintains a mapping between strings and routine closures associated with the strings.

    class MENU is
       private attr menu_actions:MAP{STR,ROUT};
            -- Hash table from strings to closures
       private attr default_action:ROUT{STR};

       create(default_act:ROUT{STR}):SAME is
          res:SAME := new;
          res.menu_actions := #MAP{STR,ROUT};
          res.default_action := default_act;
          return(res)
       end;

       add_item(name:STR, func:ROUT) is menu_actions[name] := func end;
         -- Add a menu item to the hash table, indexed by 'name'

       run is
          loop
             #OUT + ">";
             command: STR := IN::get_str;  -- Gets the next line of  input
             if command = "done" then
                break!;
             elsif menu_actions.has_ind(command) then
                menu_actions[command].call;
             else
                default_action.call(command);
             end;
          end;
       end;
    end;

We use this opportunity to create a textual interface for the calculator described earlier (See A Stack Calculator, subsection 5.1.2):

    class CALCULATOR is
       private attr stack:A_STACK{INT};
       private attr menu:MENU;

       create:SAME is
          res ::= new;
          res.init;
          return res;
       end;

       private init is   -- Initialize the calculator attributes
          stack := #;
          menu := #MENU(bind(push(_)));
          menu.add_menu_item("add",bind(add));
          menu.add_menu_item("times",bind(times));
       end;

       run is menu.run; end;

    ...

    --- Now, the main routines of the calculator computation are:

       push(s:STR) is
          -- Convert the value 's' into an INT and push it onto the stack
          -- Do nothing if the string is not a valid integer
          c: STR_CURSOR := s.cursor;
          i: INT := c.int;
          if c.has_error then
             #ERR + "Bad integer value:" + s;
          else
             stack.push(i);
          end;
       end;

       add is  -- Add the two top stack values and push/print the result
          sum:INT := stack.pop + stack.pop;
          #OUT + sum+"\n";
          stack.push(sum);
       end;

       times is  -- Multiply the top stack values and push/print the result
          product:INT := stack.pop * stack.pop;
          #OUT + product + "\n";
          stack.push(product);
       end;
    end;  -- class CALCULATOR

This calculator can be started by a simple main routine

    class MAIN is
       main is
          c:CALCULATOR := #;
          c.run;
       end;
    end;

:

After compiling the program, we can then run the resulting executable

    prompt> a.out
    >3
    >4
    >add
    7
    >10
    >11
    >times
    110
    >done
    prompt>

### 9.2.3 Iterator closures

[back] [Abstract] [Copyright Notice] [Contents] [next]

Sather - A Language Manual