Where can I use my variable?
A variable has a name and a place where that name can be used. That place is its scope. Imagine a small box inside a larger box: code can look for names in its own box and then the surrounding boxes. The small box is a child scope.
Pliro uses lexical scope: the arrangement of your written code determines those boxes. Calling a function from somewhere else does not change its surrounding scope. Local names belong inside a block or function; global names belong at program level, outside those blocks.
A declaration creates a name. Pliro makes it available from that point onwards. Some languages make later declarations available earlier, called hoisting; Pliro does not. A function can call itself, called recursion. Mutual recursion means two functions call each other; using a later function declaration this way is not supported. Prelude names are the built-in names Pliro supplies before your own declarations.
Try a name inside a block
The two variables below share a spelling but belong to different scopes. The inner name is used inside the block; the outer name is used afterwards. This is called shadowing.
# language: en
let message = "outside"
if true:
let message = "inside"
say message
say message
Predict the order before running it. You should see inside, then outside. Changing a value with set is different from declaring a new inner variable with let.
Source-order declarations
Declarations become visible in source order. A later declaration cannot be
used earlier in the same scope. A function name is visible inside its own body,
which permits direct recursion, but later functions are not generally hoisted.
function countdown(value):
if value > 0:
say value
countdown(value - 1)
Mutual recursion through a later declaration is not supported by source-order
name resolution.
Scope boundaries
Pliro uses lexical scope:
| Construct | Scope behavior |
|---|---|
| Program | Own top-level scope. |
if body |
New child scope. |
else body |
Separate new child scope. |
Each while iteration |
Fresh child scope. |
Each for iteration |
Fresh child scope containing the loop variable. |
| Function | New scope for parameters and direct body declarations. |
| Nested block | May shadow an enclosing name. |
Declaring the same name twice in one scope is an error. A child scope may shadow an outer name, as in the first example on this page.
Assignment across scopes
set searches the current and enclosing lexical scopes. It may update a
variable, parameter, or loop variable. It may not update a user function,
built-in function, or module namespace.
Built-in shadowing
Standard-library aliases are ordinary prelude names and may be shadowed:
let say = "not a function"
After that declaration, say("Hello") attempts to call the text value and
fails at runtime. Avoid reusing built-in names unless shadowing is deliberate.
