Primitive vs Complex Types

You’ve heard me talk all quarter about types and how important they are, and I’ve kept promising how we’ll talk about the differences between primitive and non-primitive types “later”. Well, at long last, “later” is now.

As a brief review, what are the “primitive” types?

What are the non-primitive types? So far, we’ve only seen two.

So let’s start this experiment with a puzzle. Guess the output of the code.

def amurrica(some_parameter):
    some_parameter = 5

def canada(some_nice_parameter):
    some_nice_parameter = 3

a = 7
print a
amurrica(a)
print a
canada(a)
print a

So what is the output of this? If you guessed

7
7
7

You’d be right! So what gives? Obviously, functions amurrica and canada are trying to change a, but why don’t they? This goes back to how parameters work in functions.

Parameters are copied, left to right, from the actual parameter to the formal parameter; this is called call by value. Wow, that’s a lot of terminology, so let’s break that down.

What’s an actual parameter? An actual parameter is the value being put inside the parentheses when the function is being called. So in this code, a is the actual parameter.

The formal parameter, on the other hand, is what is inside the def statement. It’s what the function calls whatever is being passed to it. So for the function amurrica, some_parameter is the formal parameter. Naturally, some_parameter’s scope is limited to inside of the function amurrica.

So call by value copies the value from the actual parameter to the formal one. Which means that some_parameter is a completely different variable that has a copy of whatever value a has. So when amurrica and canada try and change the formal parameters, they’re not changing a; they’re just changing the some copy of a.

Make sense? That’s call by value.

Now let’s take a look at this code instead.

def amurrica(some_parameter):
    some_parameter.append("EVERYTHING CUZ USA BE AWESOME, SON")

def canada(some_parameter):
    some_parameter.append("We have awesome Hockey, and stuff")

freedom = []
print freedom
amurrica(freedom)
print freedom
freedom = []
canada(freedom)
print freedom

Now what’s the output? You guessed it.

[]
["EVERYTHING CUZ USA BE AWESOME, SON"]
["We have awesome Hockey, and stuff"]

What!? This time the parameter changed? Before you drown your computer in a lake for fear of witchcraft, let’s look at the types of the parameters being passed. As you see, the first time we passed a primitive type. This time, we’re passing a non-primitive type. So what happens when you pass a parameter that’s non-primitive?

Here, a reference for the actual parameter is given to the formal parameter. What’s a reference? A reference is the location in your computer’s memory where the actual variable exists. This is called call by reference.

In this case, some_parameter holds the actual address of freedom; so when some_parameter makes changes, it makes changes to freedom.

Here’s the essence of the difference between primitive and complex types: primitive types are call by value, while complex types are call by reference.

But what if we wanted to change the value of a primitive type? Why not pack it in a list? Next week we’ll talk about a better way to do this, but for now this is a good enough.