Further Examples of Closures

Sather - A Language Tutorial
Prev | Chapter 9. Closures | Next

# 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 unnamedlink):

    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

An iterator closure is created that may be used to extract elements of a map that satisfy the selection criteria defined by '`select`'.

    select: ROUT{T}:BOOL;
    select_elt: ITER{MAP{E,T}}:T;
    ...
    select_elt := bind(_.filter!(select));

This creates an iterator closure that returns successive odd integers, and then prints the first ten:

    odd_ints: ITER{INT}:INT := bind(1.step!(_,2));
    loop
       #OUT + odd_ints.call!(10);
    end;

Prev | Home | Next
Closures | Up | Exceptions