Variable/Scope

From Jonathan Gardner's Tech Wiki
Jump to: navigation, search

Introduction

Given a bit of code that refers to a variable named 'x', what 'x' is it referring to? This is the problem of scoping.

No Scope Needed

You don't necessarily need scope. Some languages only have a global scope that all code refers to. This is, of course, problematic since all the functions are sharing the same variable scope. But for simple recipes (such as the bodies of simple functions), you can get away with only one scope.

>>> x = 5
>>> print(x)
5

Large programs, or programs that allow functions, outgrow this scope. There are a number of hacks to fix it, but all of them are hacky at best.

Function Scope

Most languages create a new, independent scope for every function call. When the function call completes, this scope is thrown away.

>>> def foo():
...     x = 5
...     print(x)
>>> foo()
5
>>> print(x)
NameError: 'x' undefined

Note that functions that do not define new variables do not need their own scope.

Also note that functions that accept parameters create a new scope with those parameters initialized to the values that were passed. This is a very handy shortcut.

Object Scope

Some languages have an object scope. This is a scope where variables refer to attributes in a specific object. This is what Java and C++ do, along with some other languages. Other languages prefer having a variable to represent the "this" object specifically spelled out.

Static Scope (Lexical Scope)

Static scope (aka lexical scope) is read in the place the function or block of code is defined. That is, it does not depend on how or where the function is called, just where it was originally defined.

Most languages nowadays work with static or lexical scoping.

Some languages (like Java) do it in a brain-dead way.

Example:

>>> x = 5
>>> def foo():
...     print(x)
>>> foo()
5

The x referred to is the x defined around or before foo() was defined.

For the vast majority of cases, lexical scoping is what you really need. In these cases, the global scope becomes a sort of top-level lexical scope that all lexical scopes are created from. Function scope is a new lexical scope creating from the surrounding lexical scope for each function call.

It is neat to see how this simple concept can give you so many features.

Dynamic Scope

Dynamic scope is read in the place that the function is called. This means that when you define the function or block of code, the variable does not even have to be declared yet.

Example: (Python pseudo-code)

>>> def foo():
...     print(dynamic x)
>>> x = 7
>>> foo()
7
>>> def bar():
...     x = 5
...     foo()
>>> bar()
5

Dynamic scoping is most useful for something like logging. The functions are the lowest level do not know about how logging is configured. All they care about is that there is a way to log. When you invoke these functions, you should setup dynamic variables that specify the logging mechanism.

The funky thing about dynamic scope is that you don't realize you need it until you understand what it is and recognize how it would make your job easier.

Dynamic Scope Hack

A good way to get a dynamic scope when none is available in the language is to pass around the dynamic namespace for every function call. If a function wants to start a new namespace, it passes a new one to the functions it calls. Otherwise, it passes around the one it was passed.