Class Summary: How Programs Evaluate
Copyright (c) 2017 Kathi Fisler
The textbook discusses how expressions evaluate, but not how definitions evaluate or the relationship between the definitions and interactions windows.
Now that you’re learning how to write programs, you also need to understand how Pyret will compute the results of those programs.
1 Evaluating Expressions
We say that expressions evaluate to values. The result computed by an expression is its value. The process by which an expression is computed into a value is called evaluation.
You probabaly already have an intuition for evaluation from arithmetic. In what order do you perform the operations when you evaluate
3 * (4 + (6  1)) 
? We do 6  1 first, then we add 4, then we multiply by 3. We work from the inside out, evaluating the innermost computation, replacing the expression with its value, then moving out to the next innermost expression.
The same rule—
overlayxy(circle(15, "solid", "blue"), 
25, 25, 
circle(15 * 2, "outline", "black")) 
The expression for the blue circle evaluates first, then the expression for the black circle, then the call to overlayxy, which produces the final image.
2 Evaluating Definitions
Imagine that you had the following contents in the definitions window (the one on the left):
include image 

SCOOPSIZE = 15 

cone = flipvertical(triangle(SCOOPSIZE * 2, "solid", "tan")) 

overlayxy(circle(SCOOPSIZE, "solid", "pink"), 
0, 25, 
overlayxy(circle(SCOOPSIZE, "solid", "green"), 
0, 25, 
cone)) 

# what would happen if we entered each of the following into the 
# interactions window BEFORE pressing Run? 

circle(15, "solid", "red") 
circle(SCOOPSIZE, "solid", "red") 
cone = triangle(30, "solid", "blue") 

# what would happen if we entered each of the following into the 
# interactions window AFTER pressing Run? 

SCOOPSIZE 
SCOOP 
cone = triangle(30, "solid", "blue") 
You can try these out yourself to check your answers!
Now that you see what happens, let’s understand why.
2.1 The Dictionary of Operations and Values
Under the hood, Pyret maintains what you can think of as a dictionary that maps names to values and operations. When you first start a Pyret window, the dictionary contains entries such as:
* > the multiplication operator 
/ > the division operator 
stringlength > the operator to compute the length of a string 
In the interactions window, you can write expressions using operators that are in the dictionary (Pyret also knows the number and string values, so you can use them in expressions too).
Now let’s say you want to make a circle. The circle operation isn’t in the default dictionary (Pyret keeps the dictionary small, unless you tell it otherwise). When you run
include image 
You are telling Pyret "add the image operations to the dictionary". So now the dictionary looks like:
* > the multiplication operator 
/ > the division operator 
stringlength > the operator to compute the length of a string 
circle > the operation to create circles 
... 
You can also add to the dictionary by writing definitions that associate names with values. So if you write
redcirc = circle(50, "solid", "red") 
Pyret extends the dictionary to look like:
* > the multiplication operator 
/ > the division operator 
stringlength > the operator to compute the length of a string 
circle > the operation to create circles 
... 
redcirc > the value for a solid red circle with radius 50 
2.2 Using the Dictionary
Now assume you wrote the following:
scale(10, redcirc) 
How does Pyret evaluate this expression? First, it checks that the operator (scale) exists in the dictionary (this got added when you did include image). It then evaluates the operands from left to right. It evaluates 10, which is already a value. Then it evaluates redcirc. redcirc is a name, so Pyret goes to the dictionary to look it up. The dictionary has an entry for redcirc, so Pyret grabs the associated value. Finally, Pyret uses the scale operation from the dictionary to compute a value.
When you use a name in your program, whether as an operator, and operand, or just as a value at the prompt, Pyret goes to the dictionary to look it up. If you ask for something that isn’t in the dictionary, then Pyret gives you the error saying that "this identifier is unbound". "Identifier" is the technical term for a name in a programming language. "Unbound" is the technical term for "not in the dictionary".
2.3 Evaluating Files (and Interactions)
Let’s put it all together. Go back to the ice cream cone program in the definitions window:
include image 

scoopsize = 15 

cone = flipvertical(triangle(SCOOPSIZE * 2, "solid", "tan")) 

overlayxy(circle(SCOOPSIZE, "solid", "pink"), 
0, 25, 
overlayxy(circle(SCOOPSIZE, "solid", "green"), 
0, 25, 
cone)) 
When you hit the Run button, Pyret takes the following steps:
It resets the dictionary to the initial one (with just the operators on numbers, strings, and other basic stuff we haven’t learned yet).
It goes through the definitions window, one item at a time, and evaluates each item:
If the item is an include statement, Pyret extends the dictionary.
If the item is a definition, Pyret evaluates the expression being named, then extends the dictionary with the defined name and the computed value.
If the item is an expression, Pyret evalutes the expression and displays the result in the interactions window.
While evaluating expressions, Pyret looks up values of names that are used in expressions.
Once all the items in the file have been processed, Pyret waits for you to enter expressions in the interactions window. When you do, it evaluates them (using the dictionary when needed) and displays the result of each expression.
To check your understanding of these rules, ask yourself what happens in each of the following situations:
You run the icecreamcone program, then type scoop in the interactions window
You run the icecreamcone program, then type "scoop" in the interactions window
You run the icecreamcone program, then type scoopsize + 5 in the interactions window
You run the icecreamcone program, then type scoopsize + conesize in the interactions window
You enter flavor = "strawberry" in the interactions window, hit run, then enter flavor in the interactions window.
You run the icecreamcone program, then type scoopsize = 10 in the interactions window
The last question makes a key point: you can’t give two definitions to the same name (what a confusing dictionary _that_ would be). You can edit your file (with a new value for name) and run it again, but you can’t have multiple definitions for the same name. What if you want to update the value in the dictionary while the program is running? For now, you can’t. Being able to do this complicates all sorts of things, so we will get to updating the dictionary much later in the course (this may seem odd to those who have programmed before, but there are good reasons for this which we will explain when the class as a whole gets to that point).