1. Fun, Fun, FunBeach Boys 1964: Functions
    The very first Ox program we saw, 01a-hello-world.ox, included the built-in function println(). Hopefully the role of functions like that in was intuitive enough that basic functions could be used without a lot of explanation. However, to do more in Ox and similar languages requires some explanation about how functions work. This is especially true once you begin to write your own functions.
  1. The address operator & again
  2. Recall from the chapter on addresses that & is the address operator in Ox. That is, &x is the memory location of the variable x. The operand x must be what Ox documentation calls an lvalue. In the terminology of these notes it has to be a thing. It is possible to take the address of a class object, a function, or an array element, but not of a matrix element.

    So you cannot code y = &A[2][3] so that y points at the 2,3 element of a matrix say. But you can write y = &A which means y points to the variable A which might currently contain a matrix.

    Geek TalkThe reason for these restrictions have to do with the "under-the-hood" features of Ox as a language executed by a C program and discussed in the chapter on Binding. For readers who know more about these things, an Ox pointer is really a C pointer to an oxvalue discussed in that chapter. Since an element of a matrix is simply a floating point real inside a C array it is not an oxvalue so it cannot be pointed to by an Ox pointer. Only if you were rewriting the C program that runs Ox code could you have a pointer to a specific element of a matrix.]

    If you can't point directly to A[2][3], how could you use y to get at it? This is a somewhat tricky aspect of Ox. Essentially we need to undo the address operator &. We want to access the thing y is pointing to. What makes it tricky is that this looks like accessing an element of a matrix. That is, y[0] means Look at the content of y. It is pointing to another thing. Go to that thing.

    y = &A;
    println(y[0][2][3]," = ",A[2][3]);
    
    In effect y[0] is the same as A if y contains a pointer to (or the address of) A.
  3. 999 ArgumentsThe O'Jays: Actual and Formal Arguments of a Function
  4. In an expression, f() calls the function f. That is, whatever that function does will happen. In that case it appears f has no arguments or inputs. If it does want information from the program then the values are listed inside the parentheses. We saw this immediately in 01a-hello-world.ox because that program contained the statement println("hello, world");. In other words, the string hello, world was sent or passed to the function println.

    Information passing inside a program can be confusing even if you have programmed before, because languages differ on how to pass information. In Ox all arguments are pass-by-value. To explain what that means we will work with this simple program:

    20-arguments.ox
     1:    #include "oxstd.h"
     2:    cobb(x,y,aU);
     3:    
     4:    main() {
     5:        decl two = 2.0,u=0.0, ok;
     6:        ok = cobb(two,2.5,&u);		//address of u
     7:        println("Output: ",ok," ",u);
     8:        }
     9:    
    10:    cobb(x,y,aU) {
    11:    	if (x < 0 || y < 0 )
    12:    		return 0;
    13:    	aU[0] = x^0.2 * y^0.8;
    14:    	return 1;
    15:        }
    16:    
    
    The writer of this program has defined a function named cobb, which apparently codes a two-dimensional Cobb-Douglas utility or production function. That is, $$U(x,y) = x^{.2} y^{.8}.\nonumber$$ When Ox encounters cobb(x,y) it means the programmer is declaring a function named cobb that takes two arguments. That means to Ox that this program is going to include calls to such a function so you should presume it exists. The material between the curly brackets that follow, { … }, is the definition of the function. It is what will happen when cobb() is called in the program.

    In this case the declaration and definition are in the same place. However, it is possible to separate the two. Just uncomment the second line of that file and run it again. In that version the declaration of cobb() is separate from its definition. Notice that the definition now repeats the function form, cobb(x,y). This separation can be very important in large and complex programs, because it allows a program to be processed (compiled) with just the functions used in it declared. The definitions can be brought in later when the program is linked and executed.

    In the definition of cobb() the symbols x and y are called its formal arguments. They are the names for the two things that cobb wants the program to send to it. What is listed in a call to the function are the actual arguments. Every time a function is called new actual arguments are sent. Inside the function they are used or referred to by the formal arguments. The matching up of actual arguments is in ordering. So x is the first formal argument of cobb and thus is replaced by the first actual argument, in this case two.

    Ox is a pass-by-value language. That means the value of the actual argument is sent to the function, not the argument itself. So all arguments are evaluated before the function is entered. When an array is passed, its contents may be changed by the function (unless they are const).

    Arguments to Cobb() in the Program Above
    Formal        Actual   Value Sent
    ---------------------------------
      x            two        2.0
      y            2.5        2.5
    

    To summarize, the term argument refers to an item listed in parentheses after a function name.

    Above, cobb() is called on line 7. The first value sent to it is two, a local variable inside main(). In Ox, the value of the actual argument is sent to the function and is available to the function in the corresponding formal arguments. So two is paired with the formal argument x at that call to the function. The value that x holds at the start of the function is the value of two or the integer 2.

    Python is a pass-by-reference language. That means if you are used to Python you will find Ox arguments confusing because they act differently when the function changes the argument.

  5. Visualizing Arguments
  6. Here we step through a program as a user-defined function is called, executes and returns. The value in memory related to formal and actual arguments are shown.
    Seq    Code
    -------------------------------
    2      cobb(x,y) {
    3         return x^0.2 * y^0.8;
    4          }
    
    0      main() {
              decl two = 2.0,u;
    1         u = cobb(two,2.5);
    5         println("Output: ",u);
              }
    

    The numbers are obviously not the line numbers in the code. Instead, they are the order in which lines are executed as the program runs. Remember that execution always starts in main(), and many things have been set up by the time that line is reached. We call that time 0 or RT0. Then the value of memory cells is tracked at the point the function is called (time 1). Then we move to the start of cobb(), step 2 before any of the statements in {…} happen. Time 4 is associated with }, which is the point when the return value has been set and control is returning back to the function call. Finally, time 5 is after the assignment has occurred and the function call is completed.

    Exhibit 21. Getting Results out in the return value of a function

    Now consider a slightly different program. For whatever reason, the programmer wants cobb() to return the utility of the consumption bundle $(x,y)$ in a third argument rather than as the return value of the function:
    Seq    Code
    -------------------------------
    2      cobb(x,y,u) {
    3         u = x^0.2 * y^0.8;
    4          }
    
    0      main() {
              decl two = 2.0, u = "hi";
    1         cobb(two,2.5,u);
    5         println("Output: ",u);
              }
    
    The problem: this program does not work!

    Exhibit 22. Results Can't Be Passed Back in Variables Sent as Formal Arguments

    We can see that there are two different memory cells with the label u. That's because u appears in two different decl statements. One belongs to main() and one belongs to cobb(). They are not the same thing so they occupy different places in memory. Because Ox is pass-by-value the assigned value of utility cannot get out of cobb() to be stored as the value of main::u.

    This might appear to mean that the only way to get results out of a function is through return. But there is a way: send as the actual argument the address of the location you want results to go to.

  7. Get Me Outta Here
  8. When we combine pointers (in Ox, arrays) and pass-by-value functions we get a second way to get results out of a function. The key is that the program has to send a pointer to where it wants the results sent to. Recall from above, that & is the location-of operator.
    Seq    Code
    -------------------------------
    2      cobb(x,y,u) {
    3         u[0] = x^0.2 * y^0.8;
    4          }
    
    0      main() {
              decl two = 2.0,u;
    1         cobb(two,2.5,&u);
    5         println("Output: ",u);
              }
    

    Exhibit 23. Send Results Back Through a Pointer Argument

  9. Assigning Functions to Variables
  10. We have seen how to define functions and how to call them. It is possible for an Ox variable to contain a function to be called. This also allows a function to be passed as an argument to other functions, among other uses. Here is some code that shows the idea:
    linear(x) {
      return alpha*x[0]+(1-alpha)*x[1];
      }
    cobb_douglas(x) {
      return x[0]^alpha*x[1]^(1-alpha);
      }
    decl util;
    if (utype==0)
        util = linear;
    else
        util = cobb_douglas;
    
    So apparently the variable utype holds which type of utility function to use each time the program runs. It could be Cobb-Douglas or linear. Now the rest of the code can contain util(x) to call the appropriate utility. We will see uses of this later on.

  11. Default Actual Arguments
  12. Some built in Ox functions have optional arguments. For example, the invert() function will compute the inverse of a matrix, so it could be used like this:
    decl A = < 1,0 ; -1,3 >;
    println("A inverse=",invert(A));
    
    There is another form of the same function as explained in its (simplified) Ox documentation entry:
    invert(ma);
    invert(ma, alogdet, asign);
        ma
            in: m x m real matrix A
        alogdet
            in: (optional argument) address of variable
            out: double, the logarithm of the absolute value of the determinant of A
        asign
            in: (optional argument) address of variable
            out: int , the sign of the determinant of A; 0: singular; -1,-2: negative determinant; +1,+2: positive determinant; -2,+2: result is unreliable
    
    You could also use it this way:
    decl A = < 1,0 ; -1,3 >, B, ldet;
    B = invert(A,&ldet);
    println("A inverse=",B,"log determinant=",ldet);
    
    Suppose you wanted to write your own function with this feature. You could do this by providing default actual arguments for arguments that are optional. In particular, myinvert() could be a user defined function that looks like this:
    myinvert(a,ld=0,sgn=0);    //declare the default value of arguments
    
    myinvert(a,ld,sgn)         //define the function
      {
      decl ainv;
      if (isint(ld))           // no address sent, ld is an integer
        return invert(a);
      if (isint(sgn))          // address sent for ld but not sgn
         return invert(a,ld);
      return invert(a,ld,sgn);  // addresses sent for both
      }
    
    Now you can make calls to myinvert() of these forms:
    myinvert(A);                 is the same as            myinvert(A,0,0);
    myinvert(A,&ldet);           is the same as            myinvert(A,&ldet,0);
    myinvert(A,&ldet,&sng);      is the same as            myinvert(A,&ldet,&sng);
    
    In other words, if any optional actual argument is not sent Ox will insert the default value in its place. In this case, the default values are the integers 0. So if myinvert() gets an integer value in the second argument it knows the user did not want to get the log-determinant. You cannot do this: myinvert(A,,&sng). That is, you cannot skip one actual argument and have the default argument sent for it but send your own argument for those to the right.

  13. Different Kinds of Arguments
  14. Here are the declarations of 4 different functions that accept different kinds of arguments:
    Declaration              Explanation and Example Calls
    
    f0() no arguments: f0() is the only valid call f1(x) 1 argument: f1("a") f1(0.0) f1(x) f2(x,y) 2 arguments: f2(n,m) f2(0.0,-3.0) f2("hi",{"bye"}) g1(x=1.0) 1 optional argument: g1() g1(5.0) g2(x,y=0.0) 1 required, 1 optional: g2(2.0) g2(v,-2.0) gn(x,...) 1 required, variable list: gn(5) gn(5,"a","b","c"); g0(...) none required, variable

    Notice that optional arguments have to be listed after any required arguments. It is not possible to declare h(x=2.0,y) because the call h(3.0) changes the default value of x but does set the values of the required argument y.

    The default argument and variable list "..." only appear in the function declaration. They do not appear when you call the function. And if the declaration and definition are separated the definition simply lists the arguments without defaults.

  15. Anonymous Functions
  16. Like Python and other languages, Ox has anonymous or "lambda" functions. However, we will not use these so the student should refer to the Ox documentation for explanation.
    Python and Java have "keyword arguments" but Ox does not allow this.

Exercises