Last Tech Tuesday we learned about functions as a way to define our own words in a programming language. We saw that functions take arguments and return results back to where the function was used in the program. We refer to this transfer of control of program execution from the current code to the code inside the function as a function call or a function invocation (the function is called or invoked). As promised at the end of that post, today we will take a closer look at how the arguments to a function work which will lead us to learn about variable scope.
Let’s start by examining the following bit of Javascript code (I am using Javascript a lot because anyone can run it in their own web browser and because almost every modern web page has some Javascript running on it):
If we run this code, we get the following output: 9 5. So the variable n that is the argument for the function square is different from the variable n defined outside of the function. Using n as the argument for the function left the n that was defined outside of the function unaffected. This is another example of variable scope, which we first encountered in the context of code blocks in control structures.
When a function is called, the value of the argument – here 3 – is “bound” to the variable name of the argument inside the function, here n. We say that the argument variable n is “local” to the function, whereas the n that was defined outside of any function is called a “global” variable. It is “global” in the sense that it is accessible everywhere other than functions that have an argument or a local variable called n.
Consider the following code in which we use the variable name a instead of n in the square function and also issue a print to see if we can access the value of n that was defined outside of the function
Here we have changed the name of the argument for the square function to a and are issuing a print to inspect the value of the variable n inside the function.
What should the print(n) inside the function show? This question is far from obvious and you should really think about it for a moment. There are two fundamentally different options: the first is that n is a local variable also and since it hasn’t been given a value yet it is “undefined” (in which case print might complain during execution). The second is that n refers to the global variable that was defined outside the function and currently has the value 5.
When we run the Javascript code, we see that n does indeed access the global value which is 5. If we wanted n to be a local variable without having it be one of the arguments, we would have to define it inside of the square function using the “var” reserved word as follows:
Whoa nelly! When we run this code, we get a 7 and then a 5, which means that the n inside the function is different from the n outside. So adding the “var” turned the n into a local variable which exists only while the code inside the function is executing. Now let’s just drop the var as follows:
Before you go and run this think through what we should be seeing now as the two numbers printed. If you didn’t answer 7 and 7, go back and re-read this entire post from the beginning and also re-read the posts on variables and on blocks.
So the seemingly tiny difference between having the “var” there or not makes a huge difference to what the overall program does! To recap: with “var” there, the n inside the function is local to the function. Without var there we are accessing the global n instead. Both programs execute just fine. We do not get a warning either way asking us which n we were trying to access.
And that was just Javascript. There are other languages that handle this situation exactly the other way round. What do I mean by that? Well in Javascript we had to add the keyword var to make n a local variable. In PHP by contrast any variable we refer to inside a function is automatically assumed to be local and if we want to access a variable from outside we have to do it via the “global” reserved word. Thankfully in pretty much all languages the arguments of the function are automatically local to the function!
Hopefully by now you are beginning to appreciate just how amazing it is that NASA was able to successfully land a robot the size of a car on Mars using over half a million lines of code! Even seemingly simple things in programming can be surprising and without a thorough understanding of concepts such as variable scope and argument binding it becomes difficult to write good code or understand what someone else’s code does. In fact, there is so much more to say about arguments that we will have an entire additional post on the topic next Tuesday.
In the meantime though here is a bit more for those who really like to go into detail. You may recall that in the somewhat lengthy discussion of data types, I introduced the idea that variables could be represented by a kind of internal “phone book”. For each variable name there would be an entry with the memory location at which the value of the variable can be found. Now you may wonder how we can have both local and global variables. The answer is that there isn’t a single global phone book. When a function is called we enter a new environment in which we have a local phone book available to us also. Local variables are entries in that local phone book. A lookup of a variable then proceeds by first checking in the local phone book and if it is not found there looking in the global book. Even that view is still a bit simplistic because as we will see one function can call another function or even call itself and new functions can be defined inside of other functions! So we really have a sequence of nested local phone books to deal with.
There is then also the question when and for how long these local phone books exist and how exactly they are implemented. You can see a discussion of the two options (static/lexical and dynamic) in the Wikipedia entry on scope. As we learn more about this, we will also eventually get to the name of this blog: continuations. In essence a continuation provides a way of resuming a computation exactly where it was left off, which raises more interesting questions about the values of variables.