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
PI = ~3.141592 five = num-sqrt((3 * 3) + (4 * 4)) hw = string-append("Hello", " world")
2.1.6.2 Annotated bindings
PI :: Number = ~3.141592 hw :: String = string-append("Hello", "world") this-will-fail :: Boolean = 5
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.
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:
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:
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:
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