MAXScript Language Reference > Variables - Assignment and Scope >

# Scope of Variables

Variables have an attribute called scope, which determines where in MAXScript code a variable can be accessed.

MAXScript has two kinds of variable scope, global and local.

Global variables are visible in all running MAXScript code and hold their values until you exit 3ds Max.

Local variables are directly visible to code only in the lexical scope currently in effect, and hold their values only as long as the current scope is active. Once a scope is no longer active, you can no longer access the contents of a variable local to that scope. This is similar to how most modern programming languages implement variables. The conditions under which MAXScript creates a new scope are described further on this page.

## Explicit Variable Declaration

MAXScript provides language constructs used to explicitly declare and initialize both global and local variables.

The syntax for variable declaration, <variable_decls> , is as follows:

    ( local | global ) <decl> { , <decl> }

where <decl> is defined as:

    <var_name> [ = <expr> ] -- name and optional initial value

EXAMPLES:
    global baz -- initialized to 'undefined'
    global foo=10, initialized=false -- initialized globals
    local x = 1, -- continued on several lines
    y = 2,
    z = sqrt(123.7)

As mentioned above, local variables hold their values as long as that scope is active. In nested scopes, this is usually a very short period - it starts when a function or loop or block expression runs and ends when it exits.

Each time the scope is entered at runtime, a new set of locals is created and then retired when the scope is exited.

It is a good practice to always declare variables explicitly. See further on this page for some good reasons.

## Explicit Global Variable Access

Since 3ds Max 8, MAXScript provides a way to explicitly access a variable name as global. If the variable exists in the global pool of variables, it will be used. If the variable name does not exist in the global scope, it will be created as explicitly global.

The syntax is double-colon '::' and is described in this topic.

## Local Variables as 'Private Global' variables

In certain situations, the top-level scope can remains active for extended periods and so the locals declared at that top-level hold their values for that extended period.

In particular, top-level locals declared in scripted rollouts, utilities, plug-ins, script controllers and Macro Scripts hold their values for as long as the rollout, utility, plug-in, or Macro Script exists.

Typically, they remain in existence until you redefine them, so for example when you define a Macro Script and run it for the first time, the top-level scope is created and remains active over any number of subsequent executions unless you redefine the Macro Script. At this point, the existing top-level scope is retired and a fresh one (along with its top-level locals) is created.

This lets you use top-level locals in these constructs as a kind of private global; the values they hold remain in place over many executions, but only code in that construct can see the variable and so it does not conflict with actual global variables.

Note that in scripted plug-ins, a separate copy of the top-level local scope is created for each instance of the plugin and this scope remains active while that instance remains in existence up until the end of the current 3ds Max session.

HISTORY NOTE:
In 3ds Max R2.x, the optional initialization value was applied to global variables only if the declaration creates a new variable. So, for example, if you ran the following script twice, the value of x in the second line would be 10 for the first execution, and 20 for the second execution. In 3ds Max 3 and later, the optional initialization value is always applied to global variables.
    global x = 10
    print x
    x=20
If you want to make the global variable initialization conditional in 3ds Max 3 and higher, use a construct similar to:
    global foo; if foo = = undefined do foo = 23

## Implicit Variable Declaration

If you do not explicitly declare a variable, and the variable name does not exist at a higher scope, MAXScript will create the variable the first time you use it and initialize it to hold the special value undefined . You are not required to explicitly declare a variable, or initialize its value, before you can use it. Variable names not explicitly declared by one of the previous statements are called implicitly declared variables. The scope of implicitly declared variables is the MAXScript scope context currently in effect when the variable is first used. The initial MAXScript scope context is global, and new scope contexts are opened for the following:

- Top-level open parentheses in a script file or in the Listener

- Start of a function body

- Start of a for loop body

- Start of a utility, rollout, right-click menu, Macro Script, or tool definition

- Start of a rollout, utility, right-click menu, Macro Script, or tool event handler

- Start of a when body

Within these new scopes, newly referenced variables will be implicitly declared as local variables.

Scope contexts are nested, with the scope of a variable explicitly or implicitly declared at one scope context level extending to all scope contexts below that level.

Take for example the following script:

LINE # | EXAMPLE
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    a=10 -- scope context: global
    ( b=20 -- new scope context: level 1
    for i = 1 to 5 do -- new scope context: level 2
    ( j=random i a
    k=random i b
    print (j*a+k*b)
    ) -- end of scope context: level 2
    a=a+b
    ) -- end of scope context: level 1
    print a -- back to global scope context
    print k
RESULT #1 | COMMENTS
    10
    170
    120
    290
    320
    360
    30
    30
    30
    undefined
    undefined
     result of a=10 print from i=1 print from i=2 print from i=3 print from i=4 print from i=5 return value of end of scope level 1 result of print a return value of print a result of print k return value of k
RESULT #2 | COMMENTS
    10
    170
    420
    410
    180
    160
    30
    30
    30
    5
    5
     result of a=10 result of print from loop i=1 result of print from loop i=2 result of print from loop i=3 result of print from loop i=4 result of print from loop i=5 return value at end of scope level 1 result of print a return value of print a result of print k return value of k

In the above script, variable a is first used in the global scope context, and its scope includes scope contexts global, level 1, and level 2.

Variable b is first used in scope context level 1, so it is implicitly declared as a local variable and its scope includes scope contexts level 1 and 2.

Variables i and j are first used in scope context level 2, and their scope is only scope context level 2.

The scope of variable k varies depending on whether this script has been run before.

The first time the script is run, variable k is first defined at line 5, and its scope is scope context level 2.

In line 11, variable k is used again. Because the variable k defined at line 5 is no longer in scope, a new variable k is defined whose scope is global.

The second time you run the script, at line 5 MAXScript detects variable k already exists and uses it.

So the first time you run the script line 11 will print undefined , and the second time you run the script line 11 will print the last value calculated at line 5!

NoteThe scope of the variable used as a for loop counter (variable i in the above script) is a special case. The for loop counter variable's scope is always the scope context created for the for loop. This is true even if the for loop counter variable's name has already been implicitly or explicitly declared at a higher scope context level.

## Lexical Scoping

When you explicitly declare a local variable, you can reuse the same name as a variable at a higher scope context level.

If you do this, the newly declared local variable hides the outer variables with the same name.

Any reference to that variable name later in the local variable's scope refers to the new local variable.

At the end of the local variable's scope, the next outer variable becomes visible again.

This visibility scheme is called lexical scoping. An example of lexical scoping is shown in the following script.

# | EXAMPLE
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    global foo = 23, x = 20 -- explicit declaration of foo and x,
    -- scope is global
    y = 10 -- implicit declaration of y,
    -- scope is global
    format "context level global: foo= %\n" foo
    if x > y then
    ( -- top-level open parentheses
    -- new scope is created
    local baz = foo + 1 -- uses the global foo
    local foo = y - 1 -- declares 1st local foo, hides global foo
    format "context level 1: foo= %\n" foo
    b=box() -- b implicitly declared as local
    b.pos.x = foo -- uses 1st local foo
    if (foo > 0) then
    (
    local a
    local foo = y - x -- a nested 2nd local foo, hides 1st local
    format " context level 2: foo= %\n" foo
    a = sin foo -- uses 2nd local foo
    format "a= %\n" a
    ) -- leave scope
    b.pos.y= foo - 1.5 -- back to 1st local foo
    format "context level 1: foo= %\n" foo
    ) -- leave scope
    -- b, baz and foo no longer in scope
    format "context level global: foo= %\n" foo -- back to global foo
| OUTPUT
    20 -- result of line 1
    10 -- result of line 3
    context level global - foo= 23 -- output from line 5
    OK -- result of line 5
    context level 1: foo= 9 -- output from line 11
    context level 2: foo= -10 -- output from line 18
    a= -0.173648 -- output from line 20
    context level 1: foo= -10 -- output from line 23
    OK -- result of if expression lines 6 to 24
    context level global: foo= 23 -- output from line 26
    OK -- result of line 26

This might seem a strange way of over-using a single variable name, but in large programs, these scoping rules can be useful.

For example, you may add a new user-interface item and its handlers to a utility script. By explicitly declaring the variables used in the handlers as local, you ensure these variables are independent of any preexisting variables with the same names in the remainder of the script.

When writing scripts, it is good programming practice to explicitly declare your local and global variables.

Implicit declaration is provided as a short-hand, typically used when working in the Listener interactively or developing short scripts.

When developing extended scripts, explicitly declaring variables can reduce errors and improve readability of the code.

It is also recommend that you declare as local all variables unless you really want them to be global variables. There are several reasons for this practice:

- All the variable names used in the block or function will be together, which makes it easier to find the variable names and helps prevent using incorrect names in the script.

- If more than one script is script is executed at the same time (for example, you are running a scripted utility and have a scripted controller in the scene) and both scripts use the same global variable name, the two scripts can interfere with one another. This can cause apparently random failures of one or both scripts.

- You are sure you are using the correct variable in the event that a variable with the same name has already been declared at a higher scope, and you won't inadvertently change the higher-level variable's value.

- Values of variables explicitly defined as local are displayed in error call stack trace-backs.

- In 3ds Max R2.5, if a 3ds Max class, a MAXScript method, or a MAXScript read-only global variable has the same name as an implicitly declared local variable, a MAXScript runtime error will be generated if an assignment to that variable name occurs. Since the variable name already existed with a global scope, an implicitly declared local variable was not created. Since one or more new 3ds Max classes will be created by any third party plug-ins the user has loaded, you were not be able to tell ahead of time if a variable name was a global variable name. In 3ds Max R3, if the MAXScript compiler detects that the first reference to such a variable is an assignment, it will implicitly declare a local variable, rather than leaving it as a reference to a read-only system global. For example, executing the following expression in 3ds Max R2.5 would result in a value of Box , since Box is a 3ds Max object class. In 3DS R3, MAXScript detects that the first use of variable box is an assignment and creates an implicitly declared local variable box . The result of this expression in 3ds Max R3 is the value undefined .

    (if false do box=10; box)

In the following script, an error was introduced by using undefined variable k in line 7. In the output, the error call stack trace-back shows the value for variable b in function afunc and in the block-expression calling afunc .

SCRIPT
    fn afunc =
    (
    local b
    b = box()
    for i in 1 to 10 do
    for j in 1 to 10 do
    instance b pos:[i*20,j*30,30*sin(k*36)]
    )
    --
    (
    global c=10
    local b
    b="hello"
    afunc()
    )
OUTPUT:
    afunc()
    -- Error occurred in j loop
    -- Frame:
    -- k: undefined
    -- j: 1
    -- called in i loop
    -- Frame:
    -- i: 1
    -- called in afunc()
    -- Frame:
    -- b: $Box:Box102 @ [0.000000,0.000000,0.000000]
    -- called in <block>()
    -- Frame:
    -- b: "hello"
    -- No ""*"" function for undefined
    OK

If the same script is run with lines 3 and 12 removed, the following output is generated. Because function afunc and the block-expression are in different MAXScript scope contexts, variable b is implicitly declared as local in each and contain different values. However, because they were implicitly defined, they are not included in the error call stack trace-back.

OUTPUT:
    afunc()
    -- Error occurred in j loop
    -- Frame:
    -- k: undefined
    -- j: 1
    -- called in i loop
    -- Frame:
    -- i: 1
    -- called in afunc()
    -- Frame:
    -- No ""*"" function for undefined
    OK

#### See Also

- Specifying Global Variables As Global Using ::

- Persistent Global Variables

- Type-Free Variables

- Reference Assignment

- Visibility of Locals, Functions, Structures and User-Interface Items in Rollout Code

- Accessing Locals and Other Items in a Rollout from External Code

- Memory Allocation and Garbage Collection

- Manual Garbage Collection