Functions and Scope
Functions are one of the most important parts of XE because they show how the language handles parameters, returns, recursion, and name lookup.
Defining a function
Use the fun keyword:
fun add(a, b):
return a + bCall it like this:
print(add(3, 5))Parameters
Parameters are local names inside the function body.
fun greet(name):
print("Hello " + name)
return 0XE checks argument count during semantic analysis, so calling a function with the wrong number of arguments is a compiler error.
Return values
Use return to send a value back to the caller.
fun square(n):
return n * nCurrent rules:
returnis only valid inside functions- if a function reaches the end without returning a value, or uses a bare
return, the call evaluates tonone
fun greet():
print("hi")
x = greet()
print(x) # noneScope model
Functions can access:
- their parameters
- values created inside the function body
- built-in functions
- other user-defined functions
- imported user-defined functions
- global module-level variables
Example of global access:
x = 10
fun show():
print(x)
show()Functions read the current value of a global, including changes made by top-level loops and by other functions. Lists, maps and structs are shared, so mutating one (with append, items[0] = ..., p.x = ...) changes it everywhere it is used, whether it is a global or was passed in as an argument.
items = []
fun add(x):
append(items, x)
add(1)
print(items) # [1]Local variables and global
XE follows Python's rule: a name that is assigned anywhere inside a function is local to that whole function.
- the first assignment creates it, even inside an
if,whileortryblock - later assignments reuse that same variable
- a local may have the same name as a global; the global is left untouched
fun describe(n):
if n > 0:
label = "positive"
else:
label = "not positive"
return labelA local must be assigned on every path before it is read. This is checked when compiling:
fun first_even(items):
for item in items:
if item % 2 == 0:
found = item
return found # Error: variable 'found' might not be assigned yetGive the variable a starting value (found = none) before the loop to fix it.
fun counter():
total = 0
total = total + 1
return totalTo assign to a module-level variable from inside a function, declare it with global:
count = 0
fun increment():
global count
count = count + 1
increment()
print(count) # 1Reading a name before assigning it in the same function is a compile error, because the name is local there:
count = 0
fun increment():
count = count + 1 # Error: local variable 'count' is used before it is assignedglobal can also create a module-level variable from inside a function. Using a global before any assignment to it has run is a runtime error.
Recursion
Functions can call themselves:
fun fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)Functions can be called before they are defined in the file.
Functions are values
A function can be stored in a variable, passed to another function, or returned:
fun double(x):
return x * 2
fun apply(f, items):
out = []
for item in items:
out.append(f(item))
return out
print(apply(double, [1, 2, 3])) # [2, 4, 6]
print(apply(upper, ["a", "b"])) # built-in functions work tooLambdas
lambda params: expression creates a small anonymous function:
print(apply(lambda x: x + 100, [1, 2])) # [101, 102]
fun make_adder(n):
return lambda x: x + n
add5 = make_adder(5)
print(add5(10)) # 15A lambda can use variables from around it. Their values are copied when the lambda is created, so later reassignments do not affect it (lists and maps are still shared).
Functions and structs can only be defined at the top level of a module; use a lambda when you need a function inside another one.
Method-call syntax
value.name(args) is another way to write name(value, args):
items = [3, 1, 2]
items.append(0)
print(items.sort(), "hi".upper())No contracts or constants yet
Two features that often come up in language discussions are not in XE right now:
- there is no function contract syntax such as
requiresorensures - there is no
constbinding syntax; all bindings are currently mutable
Next steps
- Continue with Runtime Behavior and Errors
- Or inspect the runnable Examples
