Variables & Scopes¶
Variables are one of the core parts of EasyLang programs.
They allow you to store values, reuse data, and pass information between different parts of your code.
In this chapter, you will learn:
- how variables are created
- how assignment works
- how EasyLang stores variables internally
- how scopes work (global, local, function, and module scopes)
- how nested blocks behave
- how name resolution works
Variable Creation¶
Variables are created using:
we let <name> = <expression>
Examples:
we let x = 10
we let name = "GreenBugX"
we let nums = [1, 2, 3]
EasyLang automatically assigns the value of the right-hand expression to the variable name.
Reassignment Example
we let x = x plus 1
This reads the old value of x, evaluates the expression, then redefines x.
Rules for Variable Names¶
Variable names must:
- start with a letter or
_ - contain only letters, digits, and
_ - not be a keyword (
we let,if,else,repeat, etc.)
Valid:
x
user_name
count2
_value
Invalid:
2x
if
true
Scope Basics¶
A scope determines where a variable is visible.
EasyLang supports:
- Global Scope
- Local Function Scope
- Inner Block Scope
- Module Scope (for imports)
The interpreter internally uses nested dictionaries to store these scopes.
Global Scope¶
Any variable declared outside of a function belongs to the global scope.
Example:
we let x = 100
so print x $ works
Global variables can be accessed anywhere except inside modules unless explicitly passed.
Local Function Scope¶
Inside functions, EasyLang creates a new inner scope.
Example:
define test(): do [
we let x = 10
so print x
]
test()
so print x $ error: x does not exist here
Variables defined inside functions do not leak into the global scope.
Function Arguments also live in local scope:
define greet(name): do [
so print "Hello, " plus name
]
name is only valid inside the function.
Block Scopes¶
Blocks created with [ ... ] also introduce their own scope.
Example:
if true then [
we let a = 5
so print a $ valid
]
so print a $ error
The variable a exists only inside the block.
Loop Blocks Also Create Scope
repeat from i = 1 to 3: do [
we let x = i mul 2
so print x
]
so print x $ error
Each iteration inherits the loop scope, but outside, x does not exist.
Name Resolution (How EasyLang Finds Variables)¶
When you use a variable:
so print x
EasyLang looks for x in:
- Local scope (function or block)
- Parent scopes (block inside block)
- Global scope
- Module scope (if using module properties)
- If not found → Runtime Error with a friendly message
Example:
we let x = 100
define demo(): do [
so print x $ found in global!
]
demo()
Shadowing Variables¶
A variable in a lower (inner) scope shadows one in a higher scope.
Example:
we let x = 10
define test(): do [
we let x = 5
so print x $ prints 5
]
test()
so print x $ prints 10
Inner scope variable overrides the outer one temporarily.
Module Scope¶
When importing a module via:
bring "math.elangh" as math
EasyLang loads the module and creates a module namespace.
Calling:
math.sqrt(25)
looks in the module’s own variable environment, not the global one.
Modules cannot access global variables unless you explicitly pass them in.
Scopes in File I/O Blocks¶
File operations do not create new scopes by themselves:
open "out.txt" as f for write
writeline f with "hello"
close f
so print f $ error: f is not a normal variable
The open keyword creates a special internal tracking object, not a variable binding in user scope.
Common Errors (and Why They Happen)¶
❌ Using a variable before defining it
so print x $ error
we let x = 10
❌ Accessing a variable outside its scope
if true then [
we let a = 50
]
so print a $ error
❌ Shadowing confusion
we let x = 10
define f(): do [
we let x = "inner"
...
]
Here, x inside the function is not the same as global x.
Example: Scopes Working Together¶
we let g = 1 $ global
define outer(): do [
we let o = 2 $ outer local
define inner(): do [
we let i = 3 $ inner local
so print g $ 1
so print o $ 2
so print i $ 3
]
inner()
]
outer()
Output:¶
1
2
3
Because EasyLang resolves names from inner → outer → global.
Summary¶
- Every variable has a scope.
- Functions and blocks create new local scopes.
- Global variables live outside all functions.
- Modules have their own namespaces.
- Inner scopes can see outer scopes, but not vice-versa.
- Name resolution moves outward until a match is found.
Next Steps¶
Continue to Functions to learn how Functions work in detail