Scope

Spot the Difference

Think about this program, what does it write?

x := 2
 x > 1 {
    x = 3
    scríobh(x)
}
scríobh(x)

Try it out now, were you right?

Now look at this program: there is a very small difference between it and the previous program. Think again about what it prints.

x := 2
 x > 1 {
    x := 3
    scríobh(x)
}
scríobh(x)

Try it out again:

Were you right?

The first program printed “3” and “3”, but the second program printed “3” and “2”. What is the difference between them?

Notice the third line in both programs:

  • The first program: x = 3.
  • The second program: x := 3.

As you know, := creates a new variable and = puts a new value in some variable. This is the difference between the two programs.

More Details

Lets look again at the first program:

x := 2
 x > 1 {
    x = 3
    scríobh(x)
}
scríobh(x)

The structure of this program is quite simple:

  1. A variable x := 2 is created on the first line.
  2. The expression x > 1 is true so the code in the block is run.
  3. The line x = 3 changes the value of x to 3.
  4. The next line writes the variable x, which equals 3, on the console.
  5. Then the last line writes x again.

Now let’s look at the second program:

x := 2
 x > 1 {
    x := 3
    scríobh(x)
}
scríobh(x)
  1. A variable x := 2 is created on the first line.
  2. The expression x > 1 is true so the code in the block is run.
  3. A new variable x := 3 is created.
  4. Next the line scríobh(x) is run, but what does x mean here? We have two variables called x. When we ran the program earlier it printed “3”, so x must mean the second variable, the one that is defined in the block.
  5. scríobh(x) is the last line too, what does x mean here? If you run the program this line will print “2”: This x must mean the x on the first line.
Scope

What is happening here?

What is Scope?

When you create a variable, what are the places in the program you can use that variable? We call these places the scope of the variable.

Generally, you can use a variable after you’ve created it, but there are some other details to be understood.

For example, look at this code:

scríobh("I live in")
country := "Ireland"
scríobh(country)

We can use the variable country on line 3 because line 3 comes after line 2. The following program is not correct because we cannot use the variable before we use it.

scríobh("I live in")
scríobh(country)
country := "Ireland"

Things get more complex when we use , le idir, nuair-a and actions.

Look at this code:

 fíor {
    x := 2
}
scríobh(x)

Is that code correct? It’s not; The scope of x is limited; When you create a variable in a block of code (code between { and }) you cannot use that variable from the outside. However, you can use it in any blocks that are nested in the block, like so:

 fíor {
    x := 2
    le i idir (0, 2) {
        scríobh(x + i)
    }
}

These rules apply to actions and outlines too. For example, think about this program, what does it write?

ainm := "Niamh"
gníomh dia_duit() {
    scríobh("Dia duit", ainm)
}

dia_duit()

ainm = "Oisín"

dia_duit()

 1 == 1 {
   ainm := "Fionn" 

   dia_duit()
}

Try it out!

Were you correct? The line ainm = "Oisín" worked, but the line ainm := "Fionn" didn’t do anything. This happens because the ainm variable in the body of dia_duit is in the scope of the first variable ainm, and the line ainm := "Fionn" creates a different variable with a different scope: It has no effect.

In this picture each unique variable has a different colour, noticed that the second ainm variable isn’t used anywhere.

Action Scope

Shadowing

What happens when we create 2 variables with the same name? We saw this already in the program at the top of the page:

x := 2
 x > 1 {
    x := 3
    scríobh(x)
}
scríobh(x)

The 4th line (scríobh(x)) looks like it’s in the scope of both x variables; The one on the first line and the one on the third line. In this case it uses the closest one (the innermost one).

It’s as if the second variable hides the first one in its scope. We call this effect “shadowing”. When we create a new variable and we use a name that is already in use by another variable, we can no longer see the old variable anymore.

However: in general it is bad practice to create a variable that hides another variable. You should use a unique name for each variable: Things get confusing when you shadow a variable with another variable.

NB: You cannot create a new variable with a name that is already in use in the same block. For example, the following code does not work:

Recursion

An action can call itself, we call this process recursion. For example, here is a program that writes “10”, “9”, “8” down to “1” on the console.

When write_number(10) is called, it writes “10” on the console, it then checks if 10 is greater than 1, which of course it is so it calls write_number(9). This writes “9” and then calls write_number(8), then that calls write_number(7), write_number(6) until it reaches write_number(1). At that point x > 1 is not true anymore so it stops.

If a variable is created in the action, a unique version of this variable is created every time the action is called. Specifically, when an action calls itself the different calls do not share their variables. Look at this program:

When we call f(2) it creates a new variable y that is equal to 2. Then it checks if x > 1 which it is. Then it calls f(1). A new variable is created call y that is equal to 1, this variable is different from the variable y we created earlier. 1 is not greater than 1 so we do not call f(0). Instead we write y on the console, which writes “1”. Now we go back to the previous call f(2) and we continue on. scríobh(y) is the next line, but y is not equal to 1 here, it’s equal to 2 because this refers to the first y we created, not the second. This then writes “2” on the console.

The Global Scope

Setanta has one special scope, the global scope. A variable has a global scope if it is defined at the top level of the program: the block at the top of the program. For example, x, mo_ghníomh and MoChreatlach in the following program have global scope. However, the variable áitiúil does not.

creatlach MoChreatlach {
    gníomh ainm() {
        scríobh("Setanta")
    }
}

gníomh mo_ghníomh() {
    áitiúil := "Dia duit"
    toradh áitiúil
}

x := 3

Global variables are special, because we can use them before we create them when we use them in an action. Lets look at some examples:

This code works: Try it out!

Code like this only works in the global scope, for example, code like this does not work. If you run it you will get an error:

We can use this to create two actions like this: This pair of actions is a bit strange, they do work but it is worth explaining how.

gníomh number_is_even(x) {
     x == 0 {
        toradh fíor
    }
    toradh number_is_odd(x - 1)
}

gníomh number_is_odd(x) {
     x == 0 {
        toradh bréag
    }
    toradh number_is_even(x - 1)
}

The action number_is_odd calls number_is_even and the action number_is_even calls number_is_odd. This works because number_is_even can reference number_is_odd even though it isn’t defined yet.

More Setanta

Congratulations! You’ve reached the end of the tutorial. Thank you so much for your interest in Setanta, I hope that you have enjoyed it.

All there is to do now is to go to the editor and try out the language. There are some examples and more documentation available on docs.try-setanta.ie.

If you need any information, or if you have any problems, you can send an email to help@try-setanta.ie.