Lecture notes: Documentation, types, and organization
Reading documentation
Let’s take a look at the documentation for Pyret’s image
library. Specifically, let’s check out the documentation for a function we
touched on briefly last week but didn’t talk about in detail:
overlay-xy
. 1
The Pyret documentation gives the following signature for overlay-xy
:
overlay-xy :: ( img1 :: Image, dx :: Number, dy :: Number, img2 :: Image ) -> Image
Image
and Number
are types, which represent categories of values. We’ve
seen examples of both types--circle(50, "solid", "red")
is an Image
, and
1/3
is a number. String
is also a type--"dog"
is a String
. We’ll see
more types as the course continues.
The signature for overlay-xy
uses these types to describe its inputs and its
outputs. When we call overlay-xy
, we’ll need to call it with an Image
, two
Number
’s, and another Image
–in that order. If we don’t, Pyret will raise an
error. These expressions are both errors:
> overlay-xy(circle(50, "solid", "red"), 10, 10) > overlay-xy(circle(50, "solid", "red"), 10, 10, "dog")
The signature provides descriptive names for its inputs to help you remember
what each of them does. For instance, dx
is the number of pixels to shift the
background image on the x
axis.
The signature also specifies a type (in this case, Image
for the operation’s
output). This determines the contexts in which you can use the operation. You
can tell from the signature that
> rotate(45, overlay-xy(circle(50, "solid", "red"), 10, 10, circle(100, "solid", "blue")))
is a valid operation, while
3 + overlay-xy(circle(50, "solid", "red"), 10, 10, circle(100, "solid", "blue"))
is not.
Code organization exercise
Here are two versions of a program to draw an alien face emoji.
Version 1:
include image base = circle(100, "solid", "green") eye = circle(18, "solid", "red") # create pair of eyes, using another square as a spacer eye-spacer = square(24, "solid", "green") eye-bar = beside(eye, beside(eye-spacer, eye)) mouth = ellipse(60, 30, "solid", "blue") # create a 3-pixel border for the base border = circle(105, "solid", "black") eye-face-spacer = rectangle(60, 30, "solid", "green") eyes-and-mouth = above(eye-bar, above(eye-face-spacer, mouth)) the-face = overlay(eyes-and-mouth, base) add-border = overlay(the-face, border) emoji = scale(0.4, add-border) emoji
Version 2:
include image base = circle(100, "solid", "green") eye = circle(18, "solid", "red") # create pair of eyes, using another square as a spacer eye-bar = beside(eye, beside(square(24, "solid", "green"), eye)) mouth = ellipse(60, 30, "solid", "blue") # create a 3-pixel border for the base # use the image-width to compute the border size border = circle((image-width(base) / 2) + 5, "solid", "black") # keep the large image so we can look at it if needed emoji-large = overlay( overlay( above(eye-bar, above(rectangle(60, 30, "solid", "green"), mouth)), base), border) # scale the large image down to the size we want emoji = scale(0.4, emoji-large) # automatically show the image when we run the file emoji
Which version do you like better, and why (class discussion)?
How about this version:
scale(0.4, overlay( overlay( above( beside( circle(18, "solid", "red"), beside( square(24, "solid", "green"), circle(18, "solid", "red"))), above( rectangle(60, 30, "solid", "green"), ellipse(60, 30, "solid", "blue"))), circle(100, "solid", "green")), circle(105, "solid", "black")))
In this course, we’ll generally encourage you to write code like Version 2. But finding the right balance between naming subcomputations and clean code structure is often challenging, and each person will do it differently! Please don’t write code like Version 3, though. Remember–programs are read by the computer, but they are also read by other people!
How Pyret evaluates expressions
Imagine that you had the following contents 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))
How will Pyret evaluate this program?
Pyret processes the expressions one at a time from top to bottom.
When Pyret encounters something of the form name = expr
, it creates an entry in
an internal dictionary. The dictionary maps names to values. Thus, Pyret
evaluates the expression on the right of the =
, then makes a dictionary entry to
associate the name with that value.
When Pyret encounters an expression that has other expressions nested within it,
it evaluates the sub-expressions from left-to-right and innermost to
outermost. Thus, in the flip-vertical
expression (that creates the cone), the
first expression to evaluate is SCOOP-SIZE
(which Pyret looks up in the
dictionary), then Pyret computes 15 * 2
, then it creates the triangle, then
flips the triangle.
In the longer overlay-xy
expression, the pink circle gets created before the
green circle (by the left-to-right rule).
You may have noticed that SCOOP-SIZE
is all in caps, while cone is in
lowercase. Conceptually, SCOOP-SIZE is a key concept in the program (which we
plan to use multiple times), whereas cone is just naming an intermediate
computation for readability. We use this naming convention to help us remember
the respective roles of the names in the program. What happens if we change
SCOOP-SIZE
?
What does the “Run” button do?
When you press the Run button, Pyret erases the dictionary, then re-processes the definitions window from the beginning. Any dictionary entries that you made only in the interactions (right) window disappear.
Expressions and statements
We’ve already seen that there’s a difference between
SCOOP-SIZE = 15
and
SCOOP-SIZE * 2
and their impact on the interactions window: the first adds to the dictionary, while the second displays a value. A piece of a program that changes how future expressions will evaluate is called a statement. Creating a name meets this definition, since expressions will yield errors or not depending on whether a name appears in the dictionary. Expressions perform computations without changing the information that Pyret maintains for running future expressions.
This distinction will become more meaningful later in the course, but we’ll use the terminology to get used to it now.
Footnotes:
overlay-xy
on the homework, though
you can if you’d like.