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

Identifiers

An identifier is a name that refers to a variable, function, type, field, or module. Identifiers are the primary mechanism for binding values to names in Forge programs.

Syntax

IdentifierIdentStart IdentContinue*

IdentStarta-z | A-Z | _

IdentContinueIdentStart | 0-9

An identifier begins with an ASCII letter or underscore, followed by zero or more ASCII letters, digits, or underscores. Identifiers have no maximum length.

x
name
_private
camelCase
snake_case
PascalCase
item2
MAX_RETRIES
__internal

Case Sensitivity

Identifiers are case-sensitive. The names name, Name, and NAME refer to three distinct bindings.

let name = "alice"
let Name = "Bob"
let NAME = "CHARLIE"
say name   // alice
say Name   // Bob
say NAME   // CHARLIE

Reserved Words

If an identifier matches a keyword string (see Keywords), it is lexed as that keyword token rather than as an Ident token. Keywords cannot be used as identifiers.

// Error: 'let' is a keyword, not a valid variable name
let let = 5  // parse error

Naming Conventions

Forge does not enforce naming conventions, but the following are idiomatic:

ElementConventionExample
Variablessnake_caseuser_name
Functionssnake_caseget_user
Types (structs)PascalCaseHttpRequest
InterfacesPascalCaseDescribable
ConstantsUPPER_SNAKEMAX_RETRIES
Modulessnake_casemath, fs, json

The it Identifier

The identifier it has special meaning inside method blocks defined with give (or impl). When used as the first parameter of a method, it refers to the receiver instance — the object on which the method was called.

thing Person {
    name: String,
    age: Int
}

give Person {
    define greet(it) {
        return "Hi, I'm " + it.name
    }
}

set p to craft Person { name: "Alice", age: 30 }
say p.greet()  // Hi, I'm Alice

When p.greet() is called, the value of p is automatically bound to it inside the method body. The caller does not pass it explicitly.

If the first parameter of a method is not named it, the method is treated as a static method — it is called on the type itself rather than on an instance:

give Person {
    define infant(name) {
        return craft Person { name: name, age: 0 }
    }
}

set baby to Person.infant("Bob")

Outside of method blocks, it has no special meaning and may be used as an ordinary identifier, though this is discouraged for clarity.

Underscore

A lone underscore (_) is a valid identifier. By convention, it is used as a placeholder for values that are intentionally ignored:

match result {
    Ok(_) => say "success"
    Err(msg) => say "error: {msg}"
}
for _, value in enumerate(items) {
    say value
}

Shadowing

A new let or set declaration may reuse an identifier that is already in scope. The new binding shadows the previous one within the inner scope:

let x = 10
say x        // 10

if true {
    let x = 20
    say x    // 20 (shadows outer x)
}

say x        // 10 (outer x is unchanged)

Shadowing creates a new binding; it does not mutate the original variable.