6 Functions

6.1 Functional Notation

Oz provides functional notation as syntactic convenience. We have seen that a procedure call:

{P X1 ... Xn R}

could be used in a nested expression as a function call:

{P X1 ... Xn}

Oz also allows functional abstractions directly as syntactic notation for procedures. Therefore, the following function definition:

fun {F X1 ... Xn} S E end

where S is a statement and E is an expression corresponds to the following procedure definition:

proc {F X1 ... Xn R} S R=E end

Warning:The exact syntax for functions as well as their transformation into procedure definitions is defined in the The Oz Notation Reference Manual.

Here we rely on the reader's intuition. Roughly speaking, the general rule for syntax formation of functions looks very similar to how procedures are formed. With the exception that, whenever a thread of control in a procedure ends in a statement, the corresponding function ends in an expression.

The program shown in Figure 6.1 is the functional equivalent to the program shown in Figure 5.7. Notice how the function AndThen/2 is unfolded into the procedure AndThen/3. Below we show a number of steps that give some intuition of the transformation process. All the intermediate forms are legal Oz programs.

fun {AndThen BP1 BP2}
   if {BP1} then {BP2}
   else false end 
end

Make a procedure by introducing a result variable B:

proc {AndThen BP1 BP2 B}
   B = if {BP1} then {BP2}
       else false end 
end

Move the result variable into the outer if-expression to make it an if-statement:

proc {AndThen BP1 BP2 B}
   if {BP1} then B = {BP2}
   else B = false end 
end


Syntax Convenience: functional notation
local 
   fun {AndThen BP1 BP2}
      if {BP1} then {BP2}
      else false end 
   end 
   fun {BinaryTree T}
      case T
      of nil then true 
      [] tree(K V T1 T2) then 
         {AndThen
          fun {$} {BinaryTree T1} end 
          fun {$} {BinaryTree T2} end}
      else false end 
   end 
end

Figure 6.1: Checking a binary tree lazily


If you are a functional programmer, you can cheer up! You have your functions, including higher-order ones, and similar to lazy functional languages Oz allows certain forms of tail-recursion optimizations that are not found in certain strict functional languages 1 including Standard ML, Scheme, and the concurrent functional language Erlang. However, standard function definitions in Oz are not lazy. Lazy functions are also supported in Oz2.

Here is an example of the well-known higher order function Map/2. It is tail recursive in Oz but not in Standard ML or in Scheme.

fun {Map Xs F}
   case Xs
   of nil then nil
   [] X|Xr then {F X}|{Map Xr F}
   end 
end 
{Browse {Map [1 2 3 4] fun {$ X} X*end}}

6.1.1 andthen and orelse

After all, we have been doing a lot of work for nothing! Oz already provides the Boolean lazy (non-strict) versions of the functions And/2 and Or/2 as the Boolean operators andthen and orelse respectively. The former behaves like the function AndThen/2, and the latter evaluates its second argument only if the first argument evaluates to false. As usual, these operators are not primitives, they are defined in Oz. Figure 6.2 defines the final version of the function BinaryTree.


fun {BinaryTree T}
    case T of nil then true  
    [] tree(K V T1 T2) then  
       {BinaryTree T1} andthen {BinaryTree T2}  
    else false end  
end

Figure 6.2: Checking a binary tree lazily


6.1.2 To Function or not to function?

Since now, in principal, we have some syntactic redundancy by either using procedures or functions, the question is when to use functional notation, and when not. The honest answer is that it is up to you! I will tell you my personal opinion. Here are some rules of thumb:


1. Strict functional languages evaluate all its argument first before executing the function
2. We will discuss them later when talking about futures and by need synchronization
3. In fact, in those cases the use of the object-oriented style of Oz is most appropriate

Seif Haridi and Nils Franzén
Version 1.4.0 (20080702)