Shadowing function parameters with local variables

Within this article I want to ask a very basic question which is important in nearly all programming languages: What if you have a function with a parameter x and inside the function you declare a local variable x. Will the local variable shadow the function parameter?

The following pseudo code shows an according example.

function Test1(x)
{
    x = x + 1;
    y = x + 1;

    return x * y;
}

function Test2(x)
{
    y = x + 1;
    x = x + 1;
    
    return x * y;
}

 

Both functions have a parameter x and a local variable x. Furthermore there is a local parameter y which will be calculated by x +2. But which of the both x values is used? The function parameter or the local one? Within the pseudo code the order of the creation of the local variables was switched in function Test2. Will both functions return the same result?

The answer is very easy: It depends!

Therefore I want to show you some examples by using C#, F# and Racket.

 

C#

If you try to implement this function in C# you get a compiler error. The following source code shows the function Test1 implemented in C#.

private static int Test1(int x)
{
    int x = x + 1;
    int y = x + 1;

    return x * y;
}

In line three the local variable x should be created. But this will result in the following error:

A local variable named ‚x‘ cannot be declared in this scope because it would give a different meaning to ‚x‘, which is already used in a ‚parent or current‘ scope to denote something else.

The C# compiler does not allow to use a local variable and a function parameter with the same name. This should prevent implementation errors. Therefore in C# you don’t have to think about the issue shown in the pseudo code because the compiler will not allow to implement such functions.

 

F#

Let’s do a second implementation of the two functions. This time by using F#. The following source code shows the two functions and according function calls to test the behaviour.

let test1 x = 
    let x = x + 1
    let y = x + 2
    x*y

let test2 x = 
    let y = x + 2
    let x = x + 1
    x*y

let a = test1 1
let b = test2 1

If you execute the application you will see the following results:

val a : int = 8

val b : int = 6

 

In F# the local variable will shadow the function parameter. Therefore the result of function Test1 is 8 because y will be created by using the local x. In the function Test2 the creation of the local variables is switched and y will be created by using the function parameter.

F# uses shadowing for variables. But as you can see this can cause different behaviours of a function and may result in implementation errors.

 

Racket

At last I want to implement the same function in Racket. The following example shows the according source code.

#lang racket
(define (test1 x) 
  (let ([x (+ x 1)] 
        [y (+ x 2)]) 
    (* x y)))

(define (test2 x) 
  (let ([y (+ x 2)]
        [x (+ x 1)]) 
    (* x y)))

 

If you call these functions with 1 as parameter you will get the following results:

> (test1 1)

6

> (test2 1)

6

 

As you can see, in Racket both functions return 6. This means the let expression, which is used in Racket to declare local variables, uses the environment before the let expression. Therefore the order of the variable declarations inside the let expression doesn’t matter.

 

Racket supports a second expression for variable declarations, the let* expression. The following source code shows two additional functions which uses the let* expression instead of the let expression.

#lang racket
(define (test3 x) 
  (let* ([x (+ x 1)] 
         [y (+ x 2)]) 
    (* x y)))

(define (test4 x) 
  (let* ([y (+ x 2)]
         [x (+ x 1)]) 
    (* x y)))

If you call these functions with 1 as parameter you will get the following results:

> (test3 1)

8

> (test4 1)

6

Now the behaviour of the functions has changed. By using the let* expression, the environment produced by the previous command is used. And therefore the local x shadows the function parameter x which results in different return values of function test3 and test4.

 

Summary

If the parameter of a function and a local variable inside of this function have the same names, you may expect the following behaviours:

  • The environment before the local variable binding is used. The function parameter stays valid.
  • The environment produced by previous commands is used. The local variable shadows the function Parameter.

Which of these two behaviours is supported depends on the used programming languages. Some languages will not allow shadowing, some will use one behaviour by default and some languages will allow you to select one of the two behaviours.

Advertisements
Dieser Beitrag wurde unter C#, F#, Racket abgelegt und mit , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Eine Antwort zu Shadowing function parameters with local variables

  1. cantaro schreibt:

    The difference between your functions in F# and in Racket is actually as follows: the F# functions define one environment containing the first variable, then another containing the second variable, in which the first variable is already used. In Racket, however, the way you defined the functions, the variables are assigned at the same time, both using the outer environment.

    You can do the same in F# as follows:

    let test3 x =
    let x, y = x + 1, x + 2
    x * y

    let test4 x =
    let y, x = x + 2, x + 1
    x * y

    Or, to have the same behaviour in Racket that you get with your F# functions, do the following:

    (define (test1 x)
    (let ([x (+ x 1)])
    (let ([y (+ x 2)])
    (* x y))))

    (define (test2 x)
    (let ([y (+ x 2)])
    (let ([x (+ x 1)])
    (* x y))))

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s