Blocks and Scopes
Blocks let you group multiple statements together inside { and } or {{ and }}. All blocks must start with => however, even if they’re standalone.
const main := fn => { print("start"); print("end");};In the most common case, a block creates a new scope. Values declared inside that scope are only available inside it.
In the example above, inner only exists inside the inner block but outer is visible to the inner block.
const main := fn => { let outer := 10;
=> { let inner := 20; print(outer); print(inner); };
// ERROR - Wouldn't work as inner is declared within a different scope print(inner);
print(outer);};Blocks can also be used as expressions that produce a value.
This is useful when you want temporary variables without leaking them into the outer scope.
let result := => { let a := 10; let b := 20; a + b;};Blocks can also be created that don’t create a new scope by using {{ and }} instead of { and }.
const main := fn => { let outer := 10;
=> {{ let inner := 20; print(outer); print(inner); }};
// Would print 20 as no new scope was created earlier print(inner);
print(outer);};Blocks can also be used in conjuction with defer.
defer runs a statement whenever the block its defined in finishes.
There is another variant of defer called defer return that will be covered later but be aware that it only runs the statement once the function its defined in finishes.
=> { defer print("leaving scope"); print("inside scope");};Calibre also supports labelled scopes and loops for more advanced control flow, but most code starts with ordinary blocks to organize logic and limit where names are visible.