Let’s continue the overview of ASPL with statements, blocks, variables, control flow, functions, and closures.
Statements
In the previous article we saw that expressions return a value. Statements on the other hand won’t return values. They carry out some action and their main job is to produce a side effect. This usually means modifying some state, reading input or writing output.
For example
print "Hello world";
is a statement that evaluates the string “Hello world” and displays the result on the screen.
There are also statements that consists of an expression and a semicolon (;
):
12 * 3; // evaluates to 36 but the result gets discarded
"Hello again"; // evaluates to 'Hello again' but the result gets discarded
They will cause the associated expression to be evaluated. They are called expression statements.
Blocks
Blocks are structures that group multiple statements into a single one. If there is a place in the
code where a single statement is expected you can provide a block containing several statements.
In ASPL blocks are wrapped in braces { ... }
.
{
print "Hello world";
print 126;
}
Variables
ASPL has both global and local variables. Variable declaration looks like this:
var x = 12;
var y = "apple";
Because we have dynamic typing you don’t have to specify the type of the variable, it will be automatically inferred, based on the type of the expression on the right hand side. It is also possible to omit the initialization.
var z;
In this case the variable will contain nil.
Once variables are declared their value can be accessed via their name. You can also assign a new value to the variable by its name.
var greeting = "Good morning";
print greeting; // Good morning
greeting = "Good afternoon";
print greeting; // Good afternoon
Control flow
During programming, sooner or later you will need to execute different code paths based on some condition, and also to repeat the same code path multiple times based on a condition. These are called control flow.
Control flow is done via special statements in ASPL, and there are three of them.
The if ()
statement executes one of its two statements based on a condition.
if ("apples" == "oranges") {
print "Apples and oranges are the same!";
} else {
print "Apples and oranges are totally different things!";
}
The while
statement executes its body as long as the condition is true.
var x = 0
while (x < 10) {
print x++;
}
And the last supported control flow statement if the for
statement.
for (var x = 0; x < 10; x++) {
print x;
}
Both for
and while
statements are basically the same thing, they just look different.
Functions
ASPL has both global and local functions. That means you can define functions in the
global scope, and also in local scopes (inside another function, for example).
Function definition is done via the func
keyword. If there are any parameters to the
function, you have to provide them inside parentheses. You can also define functions without
parameters, in this case the parentheses are empty. The body of a function is a block.
func sayHello(name) {
print "Hello " + name + "!";
}
func sayHelloToJoe() {
print "Hello Joe!";
}
Function calls look like this:
sayHello("Bob"); // Hello Bob!
sayHelloToJoe(); // Hello Joe!
If you want to return some value from the function you use the return
keyword. Functions
that does not have a return
statement will implicitly return nil
.
func sum(x, y) {
return x + y;
}
Closures
In ASPL functions are first-class, which means they are real values that you can store, retrieve and pass around as parameters to other functions.
So this will work just fine:
func addition(x, y) {
return x + y;
}
func subtraction(x, y) {
return x - y;
}
func arithmeticOperation(operation) {
return operation;
}
var sub = subtraction;
arithmeticOperation(addition)(2, 1); // 3
arithmeticOperation(sub)(2, 1); // 1
I also said that ASPL has local functions, that means you can define a function in another, for example:
func outerFunction() {
func innerFunction(x) {
print x;
}
return innerFunction(2);
}
print outerFunction(); // 2
What happens if we combine these two principles?
func outerFunction() {
var name = "Bob";
func innerFunction() {
print "Hello " + name + "!";
}
return innerFunction;
}
var greeter = outerFunction();
greeter(); // Hello Bob!
Ok, what just happened here?
We defined a function, and another inside it. The inner function
accesses a variable declared in the surrounding function. The outer function then returns a
reference to the inner function. When we call the outer function and then greeter
it will
correctly print Hello Bob!
which is the expected result.
For this to work, the inner function has to hold on to the references to any variables that was defined in one of the surrounding scopes, to make sure it lives even after the function that originally defined that variable has returned.
We call functions like innerFunction
closures,
because it “closes over” variables that are defined outside of its own scope.
Next time I will finish the overview with the introduction of classes, inheritance, and the standard library.
Stay tuned. I’ll be back.