Nullary Functions

A LEGO artifact is constructed by selecting certain kinds of bricks (e.g., 2×4 RED) and arranging them in a particular order. A Bricklayer program is constructed in a similar manner by arranging constructs from the language SML in a particular order.

Level 1 programs consist primarily of calls to Bricklayer functions. In fact, except for the first line of code, which is “open Level_1;”, every line of code in a Level 1 program is a Bricklayer function call. The first line, “open Level_1;”, is a declaration. It is this declaration that makes the Bricklayer functions belonging to Level 1 available for use in the rest of the program.

In SML, it is possible for people writing code to create their own functions. These are called user-defined functions, and they are defined using an SML langage construct known as a function declaration. In order to call a function, it must first be declared.

A function declaration consists of the following program pieces:

  1. The keyword fun which tells the compiler that what follows will be a function declaration.
  2. The name of the function. A function name typically consists of the concatenation of one or more words. For example, boat or sailBoat would be a good name for a function that, when called, builds a LEGO boat (or sailBoat).
  3. A list of formal parameters which provides a way for a function call to pass information to the function body.
  4. The reserved symbol = telling the compiler that what follows is the function body.
  5. The body of the function.
(* The general form of a function declaration. *)
fun name formal_parameters = body

(* The form of the nullary function declarations we are 
   considering in Concept 10. *)
fun name () = expression_sequence

 

Concept 10 focuses on learning how to declare functions whose (1) formal parameter lists consist of the unit value (), and (2) bodies consist of expression sequences. In general, we will refer to all functions whose formal parameter list is () as nullary functions.

The example below shows how a sequence of Bricklayer function calls can be transformed into a nullary function declaration. More specifically, program 1 consists of (1) one declaration, (2) four comments, and (3) seventeen Bricklayer Level 2 function calls – one call to build2D, one call to show2D, and the remaining calls to various put2D functions.

(* Program 1: Creating a boat using a sequence of 
   Bricklayer function calls. *)
open Level_2;

build2D(32,32); 

(* hull *)
put2D_1x1_GRAY ( 0,2);
put2D_2x2_GRAY ( 1,1);        
put2D_2x3_GRAY ( 3,0);        
put2D_2x3_GRAY ( 5,0);        
put2D_2x3_GRAY ( 7,0);        
put2D_2x3_GRAY ( 9,0);  
put2D_1x2_GRAY (11,1);   

(* mast *)
put2D_1x2_GRAY (6,3);   
put2D_1x2_GRAY (6,5);   
put2D_1x2_GRAY (6,7); 
put2D_1x1_RED  (7,8);   

(* sail *)
put2D_1x1_WHITE (2,4);   
put2D_3x2_WHITE (3,4);   
put2D_2x1_WHITE (4,6);           
put2D_1x1_WHITE (5,7);   

show2D "Boat";

 

Program 2 is obtained by structuring the code in program 1. Specifically, program 2 declares a nullary function whose name is boat. The body of boat is an expression sequence consisting of all the “put2D” function calls which were used in program 1 to create the LEGO boat. IMPORTANT, within an expression sequence a semicolon serves as a separator and not a terminator. What this means is that the symbol before the closing parenthesis “)” of the expression sequence may not be a semicolon.

We will refer to the change from program 1 to program 2 as a program transformation. A program transformation is a rule that tells you how to restructure one program to obtain another program. In this case, program 1 is restructured (aka transformed) to produce program 2.

(* Program 2: Creating a LEGO artifact via the 
   call boat(). *)
open Level_2;

(* a nullary function declaration *)
fun boat () =
    (
        (* hull *)
        put2D_1x1_GRAY ( 0,2);
        put2D_2x2_GRAY ( 1,1);        
        put2D_2x3_GRAY ( 3,0);        
        put2D_2x3_GRAY ( 5,0);        
        put2D_2x3_GRAY ( 7,0);        
        put2D_2x3_GRAY ( 9,0);  
        put2D_1x2_GRAY (11,1);   

        (* mast *)
        put2D_1x2_GRAY (6,3);   
        put2D_1x2_GRAY (6,5);   
        put2D_1x2_GRAY (6,7); 
        put2D_1x1_RED  (7,8);   

        (* sail *)
        put2D_1x1_WHITE (2,4);   
        put2D_3x2_WHITE (3,4);   
        put2D_2x1_WHITE (4,6);     
        put2D_1x1_WHITE (5,7)   
        (* NOTE: there is no final semi-colon. *) 
    );
    
build2D(32,32); 

(* To build a boat, you must call the boat function. *)
boat(); 

show2D "Boat";

 

If we do not look inside the body of boat, then program 2 consists of (1) two declarations, (2) seven comments, (3) two Bricklayer function calls — a call to build2D and a call to show2D, and (4) one call to the user-defined nullary function boat. This view of a program (i.e., the view that does not look inside the bodies of functions) is called the top-level view.

Nullary function declarations provide a basic mechanism for structuring code. The purpose of such structure is to (1) make the code easier to understand, (2) easier to modify, and (3) easier to reuse. For example, in program 1, if I wanted to build the boat twice I would need to repeat all fifteen put function calls. On the other hand, in program 2, if I wanted to build the boat twice I would simply need to simply repeat the function call boat() (nearly doubling the size of the program). Of course, in this example, calling boat would simply overwrite the first boat with the second boat. You would not see anything different. In Concept 11 we will see how an offset can be used to change this. But first we must get comfortable with declaring nullary functions and calling them.

The code for building our LEGO boat can be further structured as shown in program 3. In this example, a nullary function declaration has been written for each part of the boat: the hull, the mast, and the sail. Furthermore, two (or more) different sail functions have been declared: one that builds a white sail, and one that builds a yellow sail. And finally, two boat functions have been declared: one for building a boat with a white sail, and one for building a boat with a yellow sail. Note that (1) the both boat functions have the same hull and mast (an example of reuse), and (2) the bodies of boat1 and boat2 consist exclusively of calls to user-defined functions. The creation of user-defined function declarations whose bodies call other user-defined function declarations is an important step on the way to using the power of the computer.

(* Program 3: Creating a LEGO artifact via the 
   call boat1(). *)
open Level_2;

(* a nullary function declaration *)
fun hull () =
    (
        put2D_1x1_GRAY (0,2);
        put2D_2x2_GRAY (1,1);        
        put2D_2x3_GRAY (3,0);        
        put2D_2x3_GRAY (5,0);        
        put2D_2x3_GRAY (7,0);        
        put2D_2x3_GRAY (9,0);  
        put2D_1x2_GRAY (11,1)   
        (* NOTE: there is no final semi-colon. *)      
    );

(* a nullary function declaration *)    
fun mast () =
    (
        put2D_1x2_GRAY (6,3);   
        put2D_1x2_GRAY (6,5);   
        put2D_1x2_GRAY (6,7); 
        put2D_1x1_RED  (7,8)    
        (* NOTE: there is no final semi-colon. *)    
    );
    
(* a nullary function declaration *)    
fun whiteSail () =
    (
        put2D_1x1_WHITE (2,4);   
        put2D_3x2_WHITE (3,4);   
        put2D_2x1_WHITE (4,6);           
        put2D_1x1_WHITE (5,7)   
        (* NOTE: there is no final semi-colon. *) 
    );

(* a nullary function declaration *)    
fun yellowSail () =
    (
        put2D_1x1_YELLOW (2,4);   
        put2D_3x2_YELLOW (3,4);   
        put2D_2x1_YELLOW (4,6);           
        put2D_1x1_YELLOW (5,7)   
        (* NOTE: there is no final semi-colon. *) 
    );
    
(* a nullary function declaration *)
fun boat1 () =
    (
        hull ();    (* reused code *)
        mast ();    (* reused code *)
        whiteSail ()
    );
    
(* a nullary function declaration *)
fun boat2 () =
    (
        hull ();    (* reused code *)
        mast ();    (* reused code *)
        yellowSail ()
    );
    
build2D(32,32); 

(* To build boat1, you must 
   call the boat1 function. *)
boat1(); 

show2D "Boat";

 

The top-level view of program 3 consists of (1) seven declarations, (2) sixteen comments, (3) two Bricklayer function calls, and (4) one user-defined nullary function call. Recall in the top-level view you do not look inside the bodies of function declarations.

If we choose, we can declare one additional nullary function which includes all the function calls in program 3. We will call this all encompassing function main. The choice of this name is not arbitrary. It is inspired by languages such as Java and C which make use of a special function, called main, to start off their computations.

Program 4, shown below, is obtained by moving the three top-level function calls of program 3 into a user-defined nullary function called main. As a result, the top-level view of program 4 contains only one function call!

(* Program 4: Creating a LEGO artifact via the 
   call main(). *)
open Level_2;

(* a nullary function declaration *)
fun hull () =
    (
        put2D_1x1_GRAY (0,2);
        put2D_2x2_GRAY (1,1);        
        put2D_2x3_GRAY (3,0);        
        put2D_2x3_GRAY (5,0);        
        put2D_2x3_GRAY (7,0);        
        put2D_2x3_GRAY (9,0);  
        put2D_1x2_GRAY (11,1)   
        (* NOTE: there is no final semi-colon. *)      
    );

(* a nullary function declaration *)    
fun mast () =
    (
        put2D_1x2_GRAY (6,3);   
        put2D_1x2_GRAY (6,5);   
        put2D_1x2_GRAY (6,7); 
        put2D_1x1_RED  (7,8)    
        (* NOTE: there is no final semi-colon. *)    
    );
    
(* a nullary function declaration *)    
fun whiteSail () =
    (
        put2D_1x1_WHITE (2,4);   
        put2D_3x2_WHITE (3,4);   
        put2D_2x1_WHITE (4,6);           
        put2D_1x1_WHITE (5,7)   
        (* NOTE: there is no final semi-colon. *) 
    );

(* a nullary function declaration *)    
fun yellowSail () =
    (
        put2D_1x1_YELLOW (2,4);   
        put2D_3x2_YELLOW (3,4);   
        put2D_2x1_YELLOW (4,6);           
        put2D_1x1_YELLOW (5,7)   
        (* NOTE: there is no final semi-colon. *) 
    );
    
(* a nullary function declaration *)
fun boat1 () =
    (
        hull ();    (* reused code *)
        mast ();    (* reused code *)
        whiteSail ()
    );
    
(* a nullary function declaration *)
fun boat2 () =
    (
        hull ();    (* reused code *)
        mast ();    (* reused code *)
        yellowSail ()
    );

(* a nullary function declaration *)
fun main () =
    (
        build2D(32,32); 

        boat2 ();

        show2D "Boat"
    );
    
(* this is the only top-level function
   call in program 4 *)
main(); 

 

A powerpoint presentation that animates how nullary function declarations and calls can be introduced into a Bricklayer program can be found here.