Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Variables, constants, and scope

Names let you store values and use them again later.

Programmers often use variable to mean "a named place where a value is stored". In Acton, the more precise question is whether that name is a constant binding or a mutable one.

A constant binding keeps the same value for its lifetime. A mutable binding is one whose value may be updated later. In everyday English, mutable just means "able to change".

Where a name is defined determines where it can be used. This is called its scope.

Scope means "the part of the program where a name is visible". A name defined inside a function or method is only available there. Module-level names live across the module. Actor bodies have their own rules, which are covered below.

greeting = "hello"    # module-level constant

def show_local():
    greeting = "hi"   # local name that shadows the module-level greeting
    print("local greeting:", greeting)

def show_global():
    print("global greeting:", greeting)

actor main(env):
    show_local()
    show_global()

    env.exit(0)

In this example:

  • The module-level greeting is a constant.
  • The greeting inside show_local is a different local name.

Module-level names

Names defined at module level are constants. They are useful for helper functions, reusable values, and definitions that the rest of the module shares.

port = 9000

def address():
    return "127.0.0.1:" + str(port)

Here port is a constant: after it is defined, you use it, but you do not update it. That is a good default for names that describe configuration, helper values, and definitions shared across a module.

Local names

Names defined inside a function or method are local to that body.

def greet(name):
    message = "Hello " + name
    print(message)

Here, message only exists inside greet.

Names in actors

Acton treats names at the top level of an actor body differently from names inside a function or method.

actor Counter():
    var remaining = 3
    label = "counter"
    _unit = "items"

    def tick():
        print(label, remaining, _unit)
        remaining -= 1

In this example:

  • remaining is private mutable actor state.
  • label is a public constant attribute.
  • _unit is private to the actor because its name starts with _.

Read var as "this actor-local name will change over time". If you plan to update a value in actor code, make that explicit up front.

A plain name at the top level of an actor body is not an ordinary local variable. It becomes a constant actor attribute instead. If the name starts with _, it stays private to the actor. Without the leading underscore, other actors can read that constant through an actor reference.

Acton keeps module-level bindings constant and pushes mutable shared state into actors. That changes how you structure larger programs: refactoring stateful logic usually means introducing an actor boundary, not another mutable top-level name.

Shadowing

Shadowing means introducing a new local name with the same spelling as an existing one. The outer name still exists, but the inner one is the one used in that scope.

name = "Acton"

def show():
    name = "local"
    print(name)

show()
print(name)

Here, show introduces a local name that shadows the module-level name. Inside show, name means "local". Outside show, it still means "Acton".

Shadowing is sometimes useful, but overusing it makes code harder to read. Prefer distinct names when the two values mean different things.