On this page:
2.1.6.1 Name bindings
2.1.6.2 Annotated bindings
2.1.6.3 Shadowing
2.1.6.4 Tuple bindings
2.1.6 Bindings

Many syntactic forms in Pyret need to designate names for values. These are uniformly represented as ‹binding›s:

‹binding›: ‹name-binding› | ‹tuple-binding› ‹name-binding›: [shadow] NAME [:: ‹ann›] ‹tuple-binding›: { (‹binding› ;)* ‹binding› [;] } [as ‹name-binding›]

2.1.6.1 Name bindings

The simplest form of binding is a ‹name-binding›. This form simply associates a name with a given value:

PI = ~3.141592 five = num-sqrt((3 * 3) + (4 * 4)) hw = string-append("Hello", " world")

2.1.6.2 Annotated bindings

Slightly more complicated, a name binding may also specify an annotation, that will ensure that the value being bound has the correct type:

PI :: Number = ~3.141592 hw :: String = string-append("Hello", "world") this-will-fail :: Boolean = 5

That last line will fail at runtime with an annotation error.

Note that the annotation always comes after the name, not the value; this is not allowed, for instance:

PI = ~3.14 :: Number

2.1.6.3 Shadowing

Pyret does not permit a program to implicitly bind the same name multiple times in the same scope, as this can be confusing or ambiguous: which name was meant?

ans = 3 + 4 ans = true # did you mean to use a different name here? ans # which one was meant?

Pyret will signal an error on the second binding of ans above, saying that it shadows the earlier definition. The same rule applies to names defined in nested scopes, like functions. This program is disallowed by the shadowing rule, as well:

ans = 3 + 4 fun oops(x): ans = x * 2 # Shadows the outer ans ans end fun another-oops(ans): # Also shadows the outer ans if ans: 3 else: 4 end end

The general rule for shadowing is to look "upward and leftward", i.e. looking outward from the current scope to any enclosing scopes, to see if there are any existing bindings of the same name.

But sometimes, redefining the same name makes the most sense. In this case, a program can explicitly specify that it means to hide the outer definition, using the shadow keyword:

ans = 3 + 4 fun oops(x): shadow ans = x * 2 # <-------------------------+ ans # uses the ans defined the line above --+ end fun another-oops(shadow ans): if ans: 3 else: 3 end # uses the function's parameter end

2.1.6.4 Tuple bindings

Tuples are useful to package up several Pyret values into a single value, which can then be passed around and manipulated as a single entity. But often, the most useful manipulation is to break the tuple apart into its components. While there are ‹tuple-get› expressions to access individual components, it’s often easiest to give all the components names. We do this with a ‹tuple-binding›, which binds each component of a tuple to its own name. The number of bindings must match the length of the given tuple:

Examples:

check: {x; y} = {1; 2} x is 1 y is 2 fun sum-two({k; v}, {a; b; c}): k + v + a + b + c end sum-two({10; 12}, {1; 4; 5}) is 32 fun sum-vals(elts) block: var sum = 0 for each({k; v} from elts): sum := sum + v end sum end elts = [list: {"a"; 5}, {"b"; 6}, {"c"; 7}] sum-vals(elts) is 18 end

It is also possible to nest tuple bindings, if the tuple being bound has tuples nested inside it:

Examples:

check: {{w; x}; {y; z}} = {{~5; true}; {"hello"; 4}} w is-roughly ~5 x is true y is "hello" z is 4 end

Nested bindings likewise must match the number of components in the tuple being bound, and follow the same rules of shadowing as normal name bindings.

With nested tuples, it is sometimes also useful to not only decompose the nested tuples into their components, but to give a name to the nested tuple itself:

Examples:

check: {{w; x} as wx; {y; z} as yz} as wxyz = {{~5; true}; {"hello"; 4}} w is-roughly ~5 x is true y is "hello" z is 4 wx is-roughly {~5; true} yz is {"hello"; 4} wxyz is {wx; yz} end

As with any other name bindings, you can provide annotations on any of these components. The rule of annotations adjacent to names applies – the tuple components and the as name can have annotations. We demonstrate both permitted styles of annotation below:

check: { {w :: Number; x :: Boolean} as wx; {y; z} as yz :: {String; Number} } as wxyz = {{~5; true}; {"hello"; 4}} w is-roughly ~5 x is true y is "hello" z is 4 wx is-roughly {~5; true} yz is {"hello"; 4} wxyz is {wx; yz} end

But this is not allowed, because the {Number; Boolean} annotation is not adjacent to a name:

check: {{w; x} :: {Number; Boolean} as wx; yz} = {{~5; true}; {"hello"; 4}} w is ~5 x is true end