On this page:
4.1 The Definitions Window
4.2 Naming Values
4.2.1 Names Versus Strings
4.2.2 Expressions versus Statements
4.3 The Program Directory
4.3.1 Understanding the Run Button
4.4 Using Names to Streamline Building Images

4 Naming Values

    4.1 The Definitions Window

    4.2 Naming Values

      4.2.1 Names Versus Strings

      4.2.2 Expressions versus Statements

    4.3 The Program Directory

      4.3.1 Understanding the Run Button

    4.4 Using Names to Streamline Building Images

4.1 The Definitions Window

So far, we have only used the interactions window on the right half of the CPO screen. As we have seen, this window acts like a calculator: you type an expression at the prompt and CPO produces the result of evaluating that expression.

The left window is called the definitions window. This is where you can put code that you want to save to a file. It has another use, too: it can help you organize your code as your expressions get larger.

4.2 Naming Values

The expressions that create images involve a bit of typing. It would be nice to have shorthands so we can “name” images and refer to them by their names. This is what the definitions window is for: you can put expressions and programs in the definitions window, then use the “Run” button in CPO to make the definitions available in the interactions window.

Do Now!

Put the following in the definitions window:

include image
red-circ = circle(30, "solid", "red")

Hit run, then enter red-circ in the interactions window. You should see the red circle.

More generally, if you write code in the form:

NAME = EXPRESSION

Pyret will associate the value of EXPRESSION with NAME. Anytime you write the (shorthand) NAME, Pyret will automatically (behind the scenes) replace it with the value of EXPRESSION. For example, if you write x = 5 + 4 at the prompt, then write x, CPO will give you the value 9 (not the original 5 + 4 expression).

What if you enter a name at the prompt that you haven’t associated with a value?

Do Now!

Try typing puppy at the interactions window prompt (›››). Are there any terms in the error message that are unfamiliar to you?

CPO (and indeed many programming tools) use the phrase “unbound identifier” when an expression contains a name that has not been associated with (or bound to) a value.

4.2.1 Names Versus Strings

At this point, we have seen words being used in two ways in programming: (1) as data within strings and (2) as names for values (also called identifiers). These are two very different uses, so it is worth reviewing them.

Novice programmers frequently confuse names and strings at first. For now, remember that the names you associate with values using = cannot contain quotation marks, while word- or text-based data must be wrapped in double quotes.

4.2.2 Expressions versus Statements

Definitions and expressions are two useful aspects of programs, each with their own role. Definitions tell Pyret to associate names with values. Expressions tell Pyret to perform a computation and return the result.

Exercise

Enter each of the following at the interactions prompt:

  • 5 + 8

  • x = 14 + 16

  • triangle(20, "solid", "purple")

  • blue-circ = circle(x, "solid", "blue")

The first and third are expressions, while the second and fourth are definitions. What do you observe about the results of entering expressions versus the results of entering definitions?

Hopefully, you notice that Pyret doesn’t seem to return anything from the definitions, but it does display a value from the expressions. In programming, we distinguish expressions, which yield values, from statements, which don’t yield values but instead give some other kind of instruction to the language. So far, definitions are the only kinds of statements we’ve seen.

Exercise

Assuming you still have the blue-circ definition from above in your interactions window, enter blue-circ at the prompt (you can re-enter that definition if it is no longer there).

Based on what Pyret does in response, is blue-circ an expression or a definition?

Since blue-circ yielded a result, we infer that a name by itself is also an expression. This exercise highlights the difference between making a definition and using a defined name. One produces a value while the other does not. But surely something must happen, somewhere, when you run a definition. Otherwise, how could you use that name later?

4.3 The Program Directory

Programming tools do work behind the scenes as they run programs. Given the program 2 + 3, for example, a calculation takes place to produce 5, which in turn displays in the interactions window.

When you write a definition, Pyret makes an entry in an internal directory in which it associates names with values. You can’t see the directory, but Pyret uses it to manage the values that you’ve associated with names. If you write:

width = 30

Pyret makes a new directory entry for width and records that width has value 30. If you then write

height = width * 3

Pyret evaluates the expression on the right side of the = (width * 3), then stores the resulting value (here, 90) alongside height in the directory.

How does Pyret evaluate (width * 3)? Since width is a word (not a string), Pyret looks up its value in the directory. Pyret substitutes that value for the name in the expression, resulting in 30 * 3, which then evaluates to 90. After running these two expressions, the directory looks like:

Directory

width –> 3 height –> 90

Note that the entry for height in the directory has the result of width * 3, not the expression. This will become important as we use named values to prevent us from doing the same computation more than once.

The program directory is an essential part of how programs evaluate. If you are trying to track how your program is working, it sometimes helps to track the directory contents on a sheet of paper (since you can’t view Pyret’s directory).

Exercise

Imagine that you have the following code in the definitions window when you press the Run button:

name = "Matthias"
"name"

What appears in the interactions window? How does each of these lines interact with the program directory?

Exercise

What happens if you enter a subsequent definition for the same name, such as width = 50? How does Pyret respond? What does this tell you about the directory?

4.3.1 Understanding the Run Button

Now that we’ve learned about the program directory, let’s us discuss what happens when you press the Run button. Let’s assume the following contents are in the defintions window:

include image

width = 30
height = width * 3
blue-rect = rectangle(width, height, "solid", "blue")

When you press Run, Pyret first clears out the program directory. It then processes your file line by line, starting at the top. If you have an include statement, Pyret adds the definitions from the included library to the directory. After processing all of the lines for this program, the directory will look like:

Directory

circle –> <the circle operation> rectangle –> <the rectangle operation> ... width –> 30 height –> 90 blue-rect –> <the actual rectangle image>

If you now type at the interactions prompt, any use of an identifier (a sequence of characters not enclosed in quotation marks) results in Pyret consulting the directory.

If you now type

beside(blue-rect, rectangle(20, 20, "solid", "purple"))

Pyret will look up the image associated with blue-rect.

Do Now!

Is the purple rectangle in the directory? What about the image with the two rectangles?

Neither of these shapes is in the directory. Why? We didn’t ask Pyret to store them there with a name. What would be different if we instead wrote the following (at the interactions prompt)?

two-rects = beside(blue-rect, rectangle(20, 20, "solid", "purple"))

Now, the two-shape image would be in the directory, associated with the name two-rects. The purple rectangle by itself, however, still would not be stored in the dictionary. We could, however, reference the two-shape image by name, as shown below:

Do Now!

Imagine that we now hit the Run button again, then typed two-rects at the interactions prompt. How would Pyret respond and why?

4.4 Using Names to Streamline Building Images

The ability to name values can make it easier to build up complex expressions. Let’s put a rotated purple triangle inside a green square:

overlay(rotate(45, triangle(30, "solid", "purple")),
  rectangle(60, 60, "solid", "green"))

However, this can get quite difficult to read and understand. Instead, we can name the individual shapes before building the overall image:

purple-tri = triangle(30, "solid", "purple")
green-sqr = rectangle(60, 60, "solid", "green")

overlay(rotate(45, purple-tri),
  green-sqr)

In this version, the overlay expression is quicker to read because we gave descriptive names to the initial shapes.

Go one step further: let’s add another purple-triangle on top of the existing image:

purple-tri = triangle(30, "solid", "purple")
green-sqr = rectangle(60, 60, "solid", "green")

above(purple-tri,
  overlay(rotate(45, purple-tri),
    green-sqr))

Here, we see a new benefit to leveraging names: we can use purple-tri twice in the same expression without having to write out the longer triangle expression more than once.

Exercise

Assume that your definitions window contained only this most recent code example (including the purple-tri and green-sqr definitions, as well as the include image statement that is not shown). How many separate images would appear in the interactions window if you pressed Run? Do you see the purple triangle and green square on their own, or only combined? Why or why not?

Exercise

Re-write your expression of the Armenian flag (from Making a Flag), this time giving intermediate names to each of the stripes.

In practice, programmers don’t name every individual image or expression result when creating more complex expressions. They name ones that will get used more than once, or ones that have particular significance for understanding their program. We’ll have more to say about naming as our programs get more complicated.