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;