; last updated - 18 minutes read

Sometimes things you are familiar with because you deal with them every day turn out to be surprisingly unfamiliar one you start to think thoroughly about them. Chances are that functions and their parameter belong to this category of things. I suspect you are a programmer or a software architect (who else should be attracted to a page like "Beyond Java"?), so I'm pretty sure you pass parameters to functions on a daily basis. This is routine to you, so you don't think much about it. This article wants to show you the wealth of possible parameter passing mechanisms.

I split this article into two parts. The first part deals with the semantics, and the second part shows the syntactic sugar some programming languages offer (or fail to offer). Maybe I will add a third part dealing with the implementation of parameter passing mechanisms.

This blog is called "beyond Java", so I suppose you already know a programming language like Java. Java calls its subprograms "methods". Other programming languages use terms like "routines" or "functions".[1] Essentially, these names denote similar concepts: an algorithm is given a name, and it called by just jotting down this name. To make functions more flexible, they can take parameters and can return values.

So far, so good. So what?

Things become interesting when you start to compare different languages. The early language designers had quite a few ideas how to implement a parameter passing mechanism, many of which are lost. However, the choice of a parameter passing mechanism can influence your programming style. As we see later, entire design patterns have been invented because Java doesn't really support "Call by Value". Most people think that "Immutability" and "Defensive Copying" are good ideas. These ideas are good ideas indeed, but only because Java's lacking support of Call by Value parameters.

The well known examples

We are already talking about Java, so we might as well start with this language. In Java, you don't have much choice. There is exactly one way of passing parameters to methods. And I consider it a fairly weird one, so I would like to postpone it.

Let's start with simple examples you probably already know.

Call by Value

"Call by Value" is the default parameter passing mechanism in Pascal. The formal parameters of any function are treated as local variables. The initial value of these variables is a copy of the values that have been passed to the function. This means the algorithm inside the function is allowed to modify the variable. It can do so safely because the formal parameter is a copy of the original value. The algorithm calling the function will never see the results of the modification. The caller of the function will never be molested by any side effects caused by the functions implementation. This is a very nice feature when it comes to avoiding many hours of overtime :).

Call by Reference

Another well-known parameter passing mechanism is called "Call by Reference". This mechanism requires the calling algorithm to pass variables only. For example, if you have written a function like

convertStringToUpperCase(ByReference String text)[2]

you are allowed to write an algorithm like

String convertMe = "Once upon a time, this was a mixed case text."; convertStringToUpperCase(convertMe);

but you are not allowed to just write

convertStringToUpperCase("Once upon a time, this was a ...");

This is because the formal parameter is allowed - even expected - to be changed, and by doing so you also change the original item passed to the function. Typically the only items that can sensibly be modified are variables, so Call by Reference always enforces you to pass a variable.

Call by Reference is a popular parameter passing mechanisms in languages suffering from an underdeveloped system to return values from a function. As it happens, this includes almost every language I know except Scala (and Scala's simple and intuitive way to offer multiple return values is one of the reasons why I like Scala so much).

Call by Value - Constant and Call by Reference - Constant

These variants forbid to modify the formal parameter within the method. This is a very effective protection when we talk about Call by Value. Therefore, I frequently recommend this technique as a best practice. If we consider Call by Reference, things get worse. Declaring the formal parameter final does not prevent you from modifying the object referenced by the reference. So you shouldn't rely blindly on the keyword "const" or "final"!

Of course, it is the language designer's choice to prevent side effects even in the latter case. Unfortunately, I don't know any language that does so (but then, I know only a very small number of programming languages).

Oh - wait. Of course, I do know such a language. C++ knows method signatures with double const declarations. Depending on where you put the const keyword, you can protect the pointer, the object pointed to or both. You can read about the details in this Wikipedia article, where I also copied this example from:

/** Copied from https://en.wikipedia.org/wiki/Const-correctness */ void Foo( int * ptr, int const * ptrToConst, int * const constPtr, int const * const constPtrToConst ) { *ptr = 0; // OK: modifies the "pointee" data ptr = 0; // OK: modifies the pointer *ptrToConst = 0; // Error! Cannot modify the "pointee" data ptrToConst = 0; // OK: modifies the pointer *constPtr = 0; // OK: modifies the "pointee" data constPtr = 0; // Error! Cannot modify the pointer *constPtrToConst = 0; // Error! Cannot modify the "pointee" data constPtrToConst = 0; // Error! Cannot modify the pointer }

Java

Despite Java's popularity, I'd call Java's parameter passing mechanism sort of exotic.[3] Java uses a mixture of Call by Value and Call by Reference. Putting it in a nutshell, every parameter - be it a scalar value of an object - is passed by value. But if you pass an object, only it's reference pointer is passed. As I already mentioned, this parameter is passed by value, so you can modify it safely without risking any side effect. But there is a catch: the pointer itself still points to the original object - so this original object is subject to change. Being a seasoned teacher of Java, I am very unhappy with this parameter passing mechanism. Sooner or later, every student of mine would confuse the concepts. And I can't even blame them - Java makes it easy to confuse the concepts (you could also say that Java itself confuses the concepts). On the other hand, Java's parameter passing mechanism is very efficient. Every parameter fits into 32 bits (or 64, depending on your processor architecture), guaranteeing very fast and efficient implementations.

From an architectural and educational point of view, I personally believe this is a bad architectural decision. It's good to be able to write fast but inherently unsafe programs, but I insist that the language forces me to make this decision explicit. Implicit dangerous decisions are - well, risky, and no programmer likes risks he might have to vindicate to angry customers.

Design Patterns: Immutability and Defensive Copying

I consider it a good idea to declare parameters "final" in Java. However, as mentioned before, you should be aware that this does not prevent you from modifying the objects referenced by the parameters. The "final" keyword protects the reference pointer, but it fails to protect the object pointed to.

This strange behavior gave birth to a number of popular design patterns. In particular, there are two popular variations of "defensive copying". The idea is to copy every parameter before you use it. You can choose to copy the parameter either outside or inside the function (but you should use this choice consistently in your entire program).

An interesting variant of defensive copying is "Immutability". This idea has been made popular by functional languages like LISP or Scala, but it is a well known feature of Java itself (e.g. the String class). The idea of immutability is to never change an object. This can be achieved by declaring every object "final" (or "const" in C++, or "val" in Scala). Languages like LISP take an even more radical approach: LISP doesn't even know variables. As it turns out, you can do without variables. Whenever you want to change a variable, you just declare a new constant and assign the changed value to it.[4]

At first glance, Immutability looks like a very inefficient programming style. This is certainly true when it comes to short-term memory consumption. However, it is very easy to recognize memory that can be safely freed, so the Scala evangelists claim that this programming style is as efficient as the traditional programming styles. Putting it in a nutshell, the garbage collector has to work more often, but most of its work is done easily and quickly, so the extra work doesn't result in more time consumption. As a side effect, Immutability makes it very easy to unleash the power of multitasking, so I guess it is worth the pain.

Call by Substitution (aka Call by Name)

Do you know C or C++? Then you are used to this parameter passing mechanism, but maybe you don't know its catches. Consider the following code:

#define square(a) a*a

This defines a macro, which can rougly be compared to a function. Usually it is used exactly like a function. For example,

cout << square(8)

will print 64 to the system console. The catch is that the parameter is passed literally. Consider the following line:

int a=8; cout << square(a++);

Most language use Call by Value and therefore will print 64. C++ will print 72, because it replaces the formal parameter literally by the parameters passed to the macro. In other word, square(a++) is replaced by

cout << (a++) * (a++)

The variable a is incremented twice because the parameter ("a++") is passed literally! If you use languages like C or C++, you should be very careful not to confuse macros and functions. The are used in an identical way, but they behave differently. The reason for this different behavior is that macros are processed by the preprocessor, while functions are processed by the compiler.

Predecessors

Programmers use parameter lists every day, so they tend to take parameters for granted. However, subroutines and functions are different concepts, that have been invented independently. For example, the early BASIC dialects only knew subprograms without parameters. C64 programmers had to use global variables to pass values to subprograms. They had to be very careful in order to prevent side effects caused by two subprograms using the same variable.

This can be very tricky. You can comprehend the problem by writing the fibonacci function using subroutines without parameters. The fibonacci function is a recursive function that is defined by

fib(0) = 1 fib(1) = 1 fib(n) = fib(n-1) + fib(n-2)

Do you know how to implement this recursive algorithm without parameters?

The good news is that it's possible: both BASIC and Java are finite Turing machines, which is to say both have the same expressiveness. Every program you can write in Java can be written in BASIC, and vice versa. But in many cases, Java is much more simple and much more fun.

By the way, there is a programming language that does without parameters you use every day. In fact, every program is converted into this simple language. I'm talking about the processor's machine code. More precisely, I'm talking about the assembly language. Ironically, despite calling BASIC a high-level language and assembler a low-level language, it is very simple to pass parameters in an assembler language, while it was not that simple in the early BASIC dialects. We will have a look at the stack-oriented approach typically used by machine code programs in the next section.

Stack oriented languages

Some exotic languages like Forth use a stack as a central concept. Consequentially, passing parameters to function simply means pushing them on the stack before calling the function. This concept is a little unfamiliar, even odd, to most people, but if allows for very efficient compilers. Thus, there is a language that is far from being exotic that relies heavily on a stack: Java bytecode. To illustrate the idea, let's have a look at a simple Java method and its translation into byte code.

public static int add(int a, int b) { return a+b; } public static void main(String[] args) { int sum=add(4, 5); }

This Java program is translated into this byte code. If you are not familiar with byte code, just have a look at the comments to see what's happening on the stack.

public static add(II)I // put the top two stack elements // into variable 0 and 1 ILOAD 0 // push variable 0 on stack ILOAD 1 // push variable 1 on stack IADD // remove the top two stack elements, // add them and put the result // onto the stack IRETURN // returns the topmost stack element public static main([Ljava/lang/String;)V ICONST_4 // push the integer "4" on the stack ICONST_5 // push the integer "5" on the stack INVOKESTATIC Test.add(II)I // this method call consumes the two // topmost stack elements // and delivers the result as a // single stack element ISTORE 1 // remove the topmost stack element // and store it in variable 1 RETURN }

Passing Code to Functions

Let's start with a little introduction.

"Why should I want to pass code to a function?"

To see a practical example, have a look at the article "The Call for Closures". The article you are currently reading tries to approach the problem more theoretically.

Most languages and most programmers distinguish clearly between values and functions. There seems to be a fundamental difference between simple things like numbers and strings on the one hand and algorithms on the other hand. Even if the language offers the possibility to treat functions and values in a similar way, few people will use this feature. I suspect this gives us some insight to the way the humans brain works. However, the rise of functional languages like Groovy, Scala and Clojure changed this a little by making functional programming style and closures very popular.

However, the idea of treating functions (or algorithms in general) as first-class citizens of the language is very old.[5] In mathematics, this idea was invented some 100 years ago[6]

The C languages treats functions are first class citizens so (and as far as I know, it did so from the beginning). The citizens were called "void pointers", and this is a very powerful concept that unfortunately caused a lot of trouble. Many security leaks are caused by concepts that are similar to void pointers. A void pointer is a pointer to a function - in other words, the address of the first memory cell of the function. The bad news is that it is possible to convert every number into a void pointer, allowing you to do calculations with void pointers. Unfortunately, incrementing a void pointer by one does not add any business value to it. It just makes it invalid. You can still call it, but most likely your application will crash.

By the way, mathematicians add and multiply functions without hesitating. Obviously, there are subtle differences between functions in mathematics and functions in computer science.[7]

On the other hand, there are a lot of useful applications for passing algorithms to functions. Classical examples are:

  • Sorting lists of arbitrary data structures. In general, the programmer responsible for the sort function does not know how to compare to elements of the list. They don't have to if the comparison is a function passed as a parameter.
  • Writing a function that handles transaction management. This function is passed another function that contains the database operation. The latter function does not have to hassle with the transaction management. This problem has been solved once and forever by writing the first method.
  • The for each method languages like Groovy, Scala and - maybe - future versions of Java offer replaces most for and while loops. This loop is hidden in the implementation of the foreach() method. The body of the loop is passed as a parameter.

The latter example is particularly interesting. The foreach() method does not guarantee any order of execution. The loops are allowed to be executed forward or backward, and on multicore architectures, the loops can be executed in parallel. To unleash the power of your multicore CPU you only have to improve the foreach() method - and by doing so, you boost each of the zillions of loops your program contains. Try to achieve the same with traditional for loops!

The same idea applies to the other two examples. Fixing a bug in the transaction logic will fix it everywhere, no matter how often it is called. Switching to a more efficient sort algorithm will improve every program that uses it. Without function pointers, you have to do the trick at everywhere where something is sorted or where transaction logic is required.

Now that we know why we should pass code as a parameter, the next question is:

How do different programming languages pass code to functions?

Presently, I know two major variants and a couple of different flavors to pass code:

Function pointers

In languages like C/C++, Perl or Javascript, you can assign function pointers to variables. As far as I know, Javascript and Perl do this in a safe way: the only operation you can do with a function pointer is to store it and to execute it. In contrast, C and C++ exhibit the technical meaning of the pointer. At the end of the day, it is the address of the memory cell, so you can convert it into a 32- or 64-bit integer number. If you know your processor architecture well enough, this allows you to do weird (and even malicious) things like writing self-modifying programs. Wikipedia is a good point to start reading if you want to learn more about this interesting topic.

Classes

Object-oriented classes can simulate passing code by passing classes. The function accepts an object implementing a particular interface. To execute the code it simply calls a method defined in the interface.

Anonymous inner classes

A number of Java frameworks (e.g. Swing) rely heavily on anonymous inner classes to pass code. An anonymous inner class is just an ordinary class without a name that can access the constants and most of the variables of the surrounding class.

Closures

A closure is a similar concept. Usually, closures have a syntax that concentrates on the code to pass (in other words, it tries to reduce the syntactical overhead as far as possible). In contrast to anonymous inner classes closure have access to every variable of the surrounding code.

Java 8 Closures

At the time of writing, it is unclear how Java 8 will implement its closures. Most likely, closures will be implemented as SAMs: Simple objects consisting of a single abstract method. If so, Java closure will likely loose the possibility to modify the surrounding code's variables.


  1. This blog article will use the words function, procedure, subroutine, method and subprogram as synonyms. I certainly know there are differences between these terms, but they are not important to this blog entry, so I don't care.↩
  2. This is pseudocode. This article is about concepts, so I don't care about the exact syntax of any programming language. The only compiler I care about is your human brain. :)↩
  3. All right, I admit it - it is the same mechanism used by C, Scala and Groovy. But still, I consider it confusing.↩
  4. The LISP approach is slightly different. Instead of declaring a reusable constant you decare a function, which is evaluated more than once. However, Immutability implies that every function has a constant return value, so this return value may be cached, making the LISP implementation as efficient as the Scala implementation. The burden to write fast programs is shifted to the language and compiler designers.↩
  5. This means scalar variables, objects and functions are just the same way.↩
  6. For example, Hadamard published the ideas of a function taking a function as a parameter in 1910.↩
  7. Of course, you could easily adapt the mathematical concepts to programming languages. However, that doesn't solve any problem programmers suffer from, so it would be a waste of time. Programming and math are simply different domains.↩

Comments