2.1.7 Blocks
On this page:
2.1.7.1 Block Shorthand
2.1.7 Blocks

A block’s syntax is a list of statements:

‹block›: (‹stmt›)* ‹user-block-expr›: block: ‹block› end

Blocks serve two roles in Pyret:

The ‹let-decl›, ‹fun-decl›, ‹data-decl›, and ‹var-decl› forms are handled specially and non-locally within blocks. A detailed description of scope will appear here soon.

Blocks evaluate each of their statements in order, and evaluate to the value of the final statement in the block.

The ‹user-block-expr› form additionally creates a scope for any names bound inside it. That is, definitions within such a block are visible only within that block:

x = 10 ans = block: y = 5 + x # x is visible here 42 # value result of the block end z = y + ans # error: y is not in scope here

2.1.7.1 Block Shorthand

Many expressions in Pyret include one or more blocks within them. For example, the body of a function is defined as a block. Technically, this means the following program is legal:

fun weather-reaction(forecast, temp): ask: | forecast == "sunny" then: "sunglasses" | forecast == "rainy" then: "umbrella" | otherwise: "" end ask: | temp > 85 then: "shorts" | temp > 50 then: "jeans" | temp > 0 then: "parka" | otherwise: "stay inside!" end end

However, the program probably won’t behave as expected: rather than returning some combination of "sunglasses" and "shorts" for a warm, sunny day, it will evaluate the first ask expression, discard the result, and then evaluate the second ask expression and return its result.

Pyret will warn the programmer if it encounters programs like these, and complain that the block contains multiple expressions. Often as in this case, it signals a real mistake, and the programmer ought to revise the code to comprise a single expression — say, by concatenating the two results above. Sometimes, though, multiple expressions are deliberate:

if some-condition(): temp = some-complicated-expression() print(temp) # make sure we got it right! do-something-with(temp) else: do-something-else() end

To tell Pyret that these multiple statements are intentional, we could write an explicit block form:

if some-condition(): block: temp = some-complicated-expression() print(temp) # make sure we got it right! do-something-with(temp) end else: do-something-else() end

...but that is syntactically annoying for a straightforward situation! Instead, Pyret allows for block shorthands: writing block before the opening colon of a blocky expression signals that the expression is deliberate.

if some-condition() block: temp = some-complicated-expression() print(temp) # make sure we got it right! do-something-with(temp) else: do-something-else() end

The leading block allows for multiple statements in all of the blocks of this expression. Analogous markers exist for ‹ask-expr›, ‹cases-expr›, ‹fun-decl›, etc.

However, even this marker is sometimes too much. Suppose we eliminated the print call in the example above:

if some-condition() block: temp = some-complicated-expression() do-something-with(temp) else: do-something-else() end

Why should this expression be penalized, but the equivalent one, where we inline the definition of temp, not be? After all, this one is clearer to read! In fact, Pyret will not complain about this block containing multiple expressions. Instead, Pyret will consider the following to be valid "non-blocky" blocks:

‹non-blocky-block›: (‹stmt›)* ‹template-expr› (‹stmt›)* | (‹let-decl›)* ‹expr› | ‹user-block-expr›

Any sequence of let-bindings followed by exactly one expression is fine, as is any block containing even a single template-expression, or (obviously) an explicit block expression. All other blocks will trigger the multiple-expressions warning and require either an explicit block or a block-shorthand to fix.