Class Summary:   How Programs Evaluate
1 Evaluating Expressions
2 Evaluating Definitions
2.1 The Known Names of Operations and Values
2.2 Using the Known Names
2.3 Evaluating Files (and Interactions)

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—evaluate from the inside out—works whether your expression is over numbers, strings, images, or booleans (or other kinds of data that we will learn about later). Let’s look at this is the context of images:

  overlay-xy(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 overlay-xy, 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

  

  SCOOP-SIZE = 15

  

  cone = flip-vertical(triangle(SCOOP-SIZE * 2, "solid", "tan"))

  

  overlay-xy(circle(SCOOP-SIZE, "solid", "pink"),

    0, 25,

    overlay-xy(circle(SCOOP-SIZE, "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(SCOOP-SIZE, "solid", "red")

  cone = triangle(30, "solid", "blue")

  

  # what would happen if we entered each of the following into the

  # interactions window AFTER pressing Run?

  

  SCOOP-SIZE

  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 Known Names of Operations and Values

Under the hood, Pyret maintains what you can think of as a known names that maps names to values and operations. When you first start a Pyret window, the known names contains entries such as:

  *   --->  the multiplication operator

  /   --->  the division operator

  string-length ---> the operator to compute the length of a string

In the interactions window, you can write expressions using operators that are in the known names (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 known names (Pyret keeps the known names small, unless you tell it otherwise). When you run

  include image

You are telling Pyret "add the image operations to the known names". So now the known names looks like:

  *   --->  the multiplication operator

  /   --->  the division operator

  string-length ---> the operator to compute the length of a string

  circle ---> the operation to create circles

  ...

You can also add to the known names by writing definitions that associate names with values. So if you write

  red-circ = circle(50, "solid", "red")

Pyret extends the known names to look like:

  *   --->  the multiplication operator

  /   --->  the division operator

  string-length ---> the operator to compute the length of a string

  circle ---> the operation to create circles

  ...

  red-circ ---> the value for a solid red circle with radius 50

2.2 Using the Known Names

Now assume you wrote the following:

  scale(10, red-circ)

How does Pyret evaluate this expression? First, it checks that the operator (scale) exists in the known names (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 red-circ. red-circ is a name, so Pyret goes to the known names to look it up. The known names has an entry for red-circ, so Pyret grabs the associated value. Finally, Pyret uses the scale operation from the known names 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 known names to look it up. If you ask for something that isn’t in the known names, 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 known names".

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

  

  scoop-size = 15

  

  cone = flip-vertical(triangle(SCOOP-SIZE * 2, "solid", "tan"))

  

  overlay-xy(circle(SCOOP-SIZE, "solid", "pink"),

    0, 25,

    overlay-xy(circle(SCOOP-SIZE, "solid", "green"),

      0, 25,

      cone))

When you hit the Run button, Pyret takes the following steps:

To check your understanding of these rules, ask yourself what happens in each of the following situations:

  • You run the ice-cream-cone program, then type scoop in the interactions window

  • You run the ice-cream-cone program, then type "scoop" in the interactions window

  • You run the ice-cream-cone program, then type scoop-size + 5 in the interactions window

  • You run the ice-cream-cone program, then type scoop-size + cone-size in the interactions window

  • You enter flavor = "strawberry" in the interactions window, hit run, then enter flavor in the interactions window.

  • You run the ice-cream-cone program, then type scoop-size = 10 in the interactions window

The last question makes a key point: you can’t give two definitions to the same name. 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 known names 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 known names 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).