Comp 110 Blocks

Blocks and Scope

This page will be covering how blocks and scope are implemented in environment diagrams - If you are fuzzy on what scope means and how to trace it in code, check out our page on scope!

So...when does this matter?

Usually when we add a frame to the stack, we are showing that a function has been called and we list any variables that are within the scope of that function (i.e. parameters, variables declared inside the function body, etc.) For the most part, we'll have access to these variables throughout the function body...but what about when we don't?

Let's look at an example:

export let main = async () => {
    let i = "Hello";
    print(i); // 1
    {
        let i = "my precious";
        print(i); // 2
    }
    print(i); // 3
//pause execution here
};

main();

Oh no! We have two i variables! How do we know which one is being printed at lines 1/2/3? 

Let's make an environment diagram for the above example paused as the designated line...This is the syntax we will be using to address any variables that exist only in a nested block within a frame...this includes, if/else-if/else blocks, while loops, for loops, and general brackets.

Whenever we reach a print line, we use name resolution to decide which 'i' we should be printing! Line 1 prints "Hello", Line 2 prints "my precious", and Line 3 prints "Hello". Some notes:

  • By the time we execute Line 1, we have not executed any of the code below it, therefore "Hello" is printed!
  • Line 2 has a dilemma: it has access to two 'i's! Whenever this happens, name resolutions points us to the 'i' that is most closely accessible to the print line we are currently executing. IF there was not an 'i' declared inside the block, we would look to the block's 'parent' (in this case, the 'main' frame) for an 'i' to print. However in this case, there is an 'i' in the nested block, therefore we use that and "my precious" is printed.
  • By the time we execute Line 3, two 'i's have been declared...so which one is printed? Well the second "my precious" 'i' is only scoped to the inside of that block. When those brackets end, we no longer have access to the second 'i', therefore "Hello" is printed.

The existence of a second variable with the same name is called Variable Shadowing...

  • You CAN declare a variable of the same name in a nested, inner block
  • the inner variable is a completely separate variable from the outer variable
  • When the processor returns to the outer block, the variable name will refer solely to the original variable and the contents will be unchanged.
  • You CANNOT declare two variables with the same name in the same block...VSCode will get angry with you and its just generally not a good idea - keep your code clear and simple!
    • The name of the variable is already reserved in the current stack frame


Let's do one more example involving an if statement and a for loop:

let piratesLife = (): String => { //1
    let arrgh = ["dont", "steal", "my", "booty"]; //2
    for (let i = 0; i < arrgh.length; i++) { //3
        let treasure = "booty"; //4
        if (arrgh[i] === treasure) { //5
            let i = "aye, we've found me treasure!"; //6
            return i; //7
        } //8
    } //9
    let i = "aye, we've got to keep looking"; //10
    return i; //11
}; //12
//13
print(piratesLife()); //14

Before the frames are popped off the stack, our environment diagram will look like this:

arrgh is declared within the scope of the entire piratesLife function/frame <lines 1-11>. We will lose access to it only when the function ends

The first (type: number, value: 0) and treasure both exist within the scope of the for loops <lines 3-8>...when i increments within the for loop, THIS is the i whose value will change!

The second i (type: string, value: "aye, we've found me treasure!") exists within the scope of the if-block <lines 5-7>...when is returned inside the if-block (AKA when the booty is found), THIS is the i that will be returned.

The third i (type: string, value: "aye, we've got to keep looking") exists outside the for loop and the if-block, but within the scope of the piratesLife function <lines 1-11>...THIS is the i that will be returned when the for loop naturally ends (AKA when the booty is never found).