Variable Scopes

They gotta exist somewhere!

In the BoxLang language, many persistence and visibility scopes exist for variables to be placed in. These are differentiated by context: in a class, function, component, thread, script or template. These scopes are Java maps but enhanced to provide case-insensitivity, member functions and more. There are also different scopes that become alive depending on the execution of certain constructs, such as queries, threads, and http calls.

All BoxLang scopes are implemented as BoxLang structures, basically case-insensitive maps behind the scenes.

📋 Table of Contents

When you declare variables in templates, classes, or functions, they’re stored within a structured scope. This design makes your code highly flexible, allowing you to fluently interact with the entire scope through a wide range of BIFs. You can even bind these variables directly to function calls, attributes, and more—highlighting the dynamic power at the core of BoxLang.

The only language keyword that can be used to tell the variable to store in a function's local scope is called var. However, you can also just use the localscope directly or none at all. However, we do recommend being explicit on some occasions to avoid ambiguity.

📄 Template/Scripts (bxm,bxs)

All scripts and templates have the following scopes available to them. Please note that the variablesscope can also be implicit. You don't have to declare it.

  • variables - The default or implicit scope to which all variables are assigned.

⚡Templates/Scripts Function Scopes

All user-defined functions declared in templates or scripts will have the following scopes available:

  • variables - Has access to private variables within a Class or Page

  • local - Function-scoped variables only exist within the function execution. Referred to as var scoping. The default assignment scope in a function.

  • arguments - Incoming variables to a function

🗳️ Classes (bx)

All classes in BoxLang follow Object-oriented patterns and have the following scopes available. Another critical aspect of classes is that all declared functions will be placed in a visibility scope.

  • variables - Private scope, visible internally to the class only

  • this - Public scope, visible from the outside world and a self-reference internally

  • static - Store variables in the classes blueprint and not the instance

  • super- Only available if you use inheritance

Function Scopes

Depending on a function’s visibility, BoxLang automatically places a reference (or pointer) to that function in different scopes, within the class ensuring that it can be discovered and invoked appropriately. Why does it do this? Because BoxLang is a dynamic language by design.

In practice, this means that functions in BoxLang are not locked down at compile time. Instead, they remain fully malleable at runtime—you can add new functions, remove existing ones, or even modify their behavior on the fly. This flexibility allows developers to build more adaptive and expressive applications, where behaviors can evolve based on context, configuration, or user interaction.

By managing function pointers across scopes, BoxLang ensures that these runtime changes are immediately reflected wherever the function is expected to live. Whether it’s within a local scope, a component, or a broader application context, the dynamic nature of BoxLang makes it possible to treat functions as living members of your application rather than static, unchangeable artifacts.

  • Private Function

    • variables

  • Public or Remote Functions

    • variables

    • this

⚡Closures

All closures in BoxLang are context-aware, which means they carry with them knowledge of the environment in which they were created. In other words, a closure isn’t just a function—it’s a function plus the scope it was born in. This allows closures to maintain access to variables, functions, and references that existed at the time they were defined, even if they are later executed in a completely different context.

For example, if you define a closure inside a function, it will continue to “remember” the local variables of that function, making it a powerful tool for encapsulating state and creating reusable logic without polluting the global or class-level scopes.

When closures are defined inside classes, they become even more useful. In this case, closures automatically capture the class’s variables scope (the private, per-instance data) as well as the this scope (the public interface of the instance). This means closures have seamless access to both the internal state of the class and its public methods, enabling elegant encapsulation of behavior that can directly interact with the object’s data.

This context-awareness is what makes closures in BoxLang so flexible. They’re not isolated pieces of code; they are “living” functions that carry their birthplace with them. This ensures that wherever you pass them—whether into a higher-order function, as an event handler, or across asynchronous execution—they still know exactly where they came from and what they can interact with.

  • variables - Has access to private variables from where they were created (Classes, scripts or templates)

  • this - Has access to public variables from where they were created (Classes)

  • local - Function-scoped variables only exist within the function execution. Referred to as var scoping. The default assignment scope in a function.

  • arguments - Incoming variables to a function

🪶 Lambdas

Lambdas in BoxLang are designed to be pure functions. Unlike closures, they do not capture or carry along the scope in which they were defined. In other words, lambdas don’t “remember” their birthplace or have access to surrounding variables. They exist independently of the context in which they were created.

This distinction has two major implications:

  1. Simplicity – Because lambdas are context-free, they are lightweight and predictable. They only operate on the arguments explicitly passed to them and any values they define internally. You don’t have to worry about hidden dependencies or external scope leaks. This makes them ideal for short, focused operations like transformations, mappings, or small inline computations.

  2. Performance – Without the overhead of capturing surrounding scopes, lambdas are faster to create and execute compared to closures. They don’t need to carry around extra baggage such as references to variables, this, or parent function contexts. This efficiency makes them an excellent choice in performance-sensitive scenarios, especially when used in large iterations, functional pipelines, or hot paths in your application.

In essence, lambdas in BoxLang provide a lean, functional style of programming: self-contained, efficient, and free of side effects. They shine when you need a quick function without any ties to the broader context—perfect for concise logic that should remain pure and context-independent.

  • local - Function-scoped variables only exist within the function execution. Referred to as var scoping. The default assignment scope in a function.

  • arguments - Incoming variables to a function

The following will throw an exception as it will try to reach outside of itself:

🏷️ Components

In BoxLang, you’re not limited to the built-in components—you can extend the language itself by creating your own custom components. These components can be invoked seamlessly in both script syntax and templating syntax, giving developers a consistent way to encapsulate and reuse functionality across different coding styles.

Custom components are powerful because they allow you to wrap up behavior into a self-contained unit that can handle input, manage its own internal state, and even communicate back to the calling template. This makes them excellent for building reusable UI constructs, workflow blocks, or domain-specific abstractions. They also do not affect the parser, but rather are invoked at runtime. This means, that you can create predictable, reusable components without worrying about breaking the syntax of your BoxLang code.

Every custom/core component automatically has access to a set of special scopes that give it flexibility and power:

attributes – Holds the incoming arguments or attributes passed into the component. This is how you provide input to customize the component’s behavior. • variables – Acts as the default scope for variable assignments inside the component, ensuring that the component has its own sandboxed data environment. • caller – Provides a way for the component to interact with the template that invoked it, allowing you to set or read variables from the caller’s context. This enables two-way communication between the component and the outside world.

Example Usage

You can call a component using script syntax, where it looks and feels like a function call with a block of content:

Or you can invoke it using templating syntax, which is closer to traditional custom tags:

🧵 Thread

When you create threads with the threadcomponent, you will have these scopes available to you:

  • attributes - Passed variables via a thread

  • thread - A thread-specific scope that can be used for storage and retrieval. Only the owning thread can write to this scope. All other threads and the main thread can read the variables.

  • local - Variables local to the thread context

  • variables - The surrounding declared template or class variablesscope

  • this- The surrounding declared class scope

🧵 bxThread

The bxThread scope is a special BoxLang scope that provides access to thread metadata and variables from anywhere in your request. It contains a key for every thread that has been executed in the current request, making it easy to access thread data from outside the thread context.

  • bxThread - Global scope containing all threads executed in the current request

  • Each thread key contains metadata and variables set from within that thread

🌐 Runtime Scopes

BoxLang runs in different runtime contexts, and the available scopes depend on which runtime you're using. Understanding these differences is crucial for building portable applications.

🖥️ Core OS

When running BoxLang in CLI mode or core OS runtime, you have access to these scopes:

  • variables - Default scope for variable assignments

  • local - Function-scoped variables

  • arguments - Function arguments

  • application - Application-wide persistence (requires Application.bx)

  • session - Session persistence (requires Application.bx)

  • request - Request-scoped variables

  • server - Server-wide persistence across all applications

  • thread - Thread-specific variables

  • bxThread - Global thread metadata scope

🕸️ Web Runtimes

When running in web runtimes (Servlet, MiniServer, Lambda, Desktop), you get additional web-specific scopes:

All Core Scopes PLUS:

  • url - HTTP GET parameters from the URL

  • form - HTTP POST form data

  • cgi - Server and request environment variables

  • cookie - Browser cookies

  • bxFile - File upload information (during file uploads)

  • bxHttp - HTTP request/response data (during HTTP operations)

Runtime Detection: You can check your runtime context using server.boxlang.runtime.name to conditionally access web scopes.

🔍 Unscoped Variables

If you use a variable name without a scope prefix, BoxLang checks the scopes in the following order to find the variable (When we say function it includes closures, UDFs and lambdas):

  1. Local (Functions only)

  2. Arguments (Functions only)

  3. Attributes (Components only)

  4. Thread local (inside threads only)

  5. Query (variables in active query loops)

  6. Thread

  7. Variables

If the runtime is web-based, it will also check these scopes last:

  1. CGI

  2. URL

  3. Form

  4. Cookie

🔍 Query Loop Scope

When you iterate over a query using bx:loop or functional methods like queryEach(), BoxLang automatically registers the query with the current context and makes the column names available as unscoped variables for the duration of each iteration.

Query Loop Variables

During query iteration, these special variables and functions are available:

Variable/Function
Description

Column Names

All query column names are available as unscoped variables containing the current row's value

queryCurrentRow(query)

Returns the current row number (1-based)

currentRow

Alias for current row number (when using member function syntax)

columnArray

Array of column names in the query

columnList

Comma-separated list of column names

recordCount

Total number of rows in the query

Query Loop Behavior

Member Function Syntax

Performance Tip: When accessing query columns in loops, unscoped column names are resolved through the query loop scope, which is efficient. However, if you need to access variables from outer scopes, be explicit with your scoping to avoid ambiguity.

Try query loops on try.boxlang.io

⚠️ Catch Scope

When an exception is caught in a try/catch block, BoxLang creates a special catch context that makes the exception variable available. The exception variable contains detailed information about the error.

Exception Variable Structure

The exception variable (commonly named e, ex, or error) is a struct containing:

Property
Description

message

The error message

type

The exception type (e.g., "Application", "Database", "Expression")

detail

Detailed error information

stackTrace

Full stack trace as a string

tagContext

Array of stack frames with file/line information

cause

The underlying Java exception (if applicable)

errorCode

Error code (if applicable)

extendedInfo

Additional error information (if applicable)

Catch Scope Behavior

CFML Compatibility

In CFML compatibility mode (using the bx-compat-cfml module), the exception variable is also available as cfcatch:

Rethrowing Exceptions

Within a catch block, you can use the rethrow statement to re-throw the current exception:

Try exception handling on try.boxlang.io

🔒 Final Variables

BoxLang supports the final modifier for variables in any scope, which prevents the variable from being reassigned after its initial assignment. This is useful for creating constants and preventing accidental modifications.

💾 Persistence Scopes

Can be used in any context, used for persisting variables for a period of time.

  • session - stored in server RAM or external storages tracked by unique web visitor

  • application - stored in server RAM or external storage tracked by the running BoxLang application

  • cookie - stored in a visitor's browser

  • server - stored in server RAM for ANY application for that BoxLang instance

  • request - stored in RAM for a specific user request ONLY

  • cgi - read only scope provided by the servlet container and BoxLang

  • form - Variables submitted via HTTP posts

  • URL - Variables incoming via HTTP GET operations or the incoming URL

Practical Persistence Examples

Scope Interaction Examples

Scope Debugging & Performance

Performance Tip: Always scope your variables explicitly. Unscoped variables trigger scope hunting, which impacts performance, especially in complex applications.

🖥️ Server Scope

The server scope is a global scope that lives for the entire lifetime of the BoxLang runtime instance. It contains system information, BoxLang runtime details, and can store custom variables that need to persist across all applications and requests. It contains the following sub-scopes:

  • boxlang - BoxLang runtime information

  • cli - Command line execution details

  • java - Java runtime information

  • os - Operating system information

  • separator - File system separators

  • system - System properties and environment

🏗️ Server Scope Structure

The server scope is automatically populated with several unmodifiable sub-structures:

🎯 BoxLang Information (server.boxlang)

Key
Type
Description

buildDate

string

Build date of the BoxLang runtime

boxlangId

string

Unique identifier for this BoxLang instance

codename

string

Release codename for this version

cliMode

boolean

Whether running in CLI mode

debugMode

boolean

Whether debug mode is enabled

jarMode

boolean

Whether running from JAR file

modules

struct

Information about loaded modules

runtimeHome

string

Path to BoxLang runtime directory

version

string

BoxLang version number

🏢 Operating System Information (server.os)

Key
Type
Description

additionalinformation

string

Additional OS information

arch

string

System architecture (x86, x64, etc.)

archModel

string

Architecture model

hostname

string

Local machine hostname

ipAddress

string

Local machine IP address

macAddress

string

Local machine MAC address

name

string

Operating system name

version

string

Operating system version

☕ Java Runtime Information (server.java)

Key
Type
Description

archModel

string

Architecture model

availableLocales

array

List of available locales

defaultLocale

string

Default system locale

executionPath

string

Java execution path

freeMemory

numeric

Available free memory in bytes

maxMemory

numeric

Maximum memory available in bytes

totalMemory

numeric

Total memory allocated in bytes

vendor

string

Java vendor name

version

string

Java version number

📁 File System Separators (server.separator)

Key
Type
Description

file

string

File separator character (/ or \)

line

string

Line separator character(s)

path

string

Path separator character (; or :)

💻 CLI Information (server.cli)

Key
Type
Description

args

array

Original command line arguments

command

string

Full command line used to start BoxLang

executionPath

string

Directory from which CLI was executed

parsed

struct

Parsed command line arguments

⚙️ System Properties and Environment (server.system)

Key
Type
Description

environment

struct

System environment variables (if security allows)

properties

struct

Java system properties (if security allows)

🔧 Custom Server Variables

You can store your own variables in the server scope for cross-application persistence:

🔒 Unmodifiable Keys

The following keys cannot be modified once the server scope is initialized:

  • boxlang - BoxLang runtime information

  • os - Operating system information

  • java - Java runtime information

  • separator - File system separators

  • system - System properties and environment

🌐 CGI Scope

The CGI scope is a read-only scope available only in web runtimes that contains HTTP request information and server environment variables. It provides access to web server and request details following the Common Gateway Interface standard.

📡 Request Information

🖥️ Server Information

👤 Client Information

📂 File Path Information

🌐 HTTP Headers

The CGI scope automatically provides access to all HTTP headers using the http_ prefix:

🔍 CGI Scope Behavior

🔐 Security and SSL Information

📊 Available CGI Variables

The CGI scope provides access to these standard variables:

Variable
Description

auth_password

HTTP authentication password

auth_type

HTTP authentication type

auth_user

HTTP authenticated username

bx_template_path

BoxLang template physical path

cf_template_path

CFML-compatible template path

content_length

Request content length

content_type

Request content type

context_path

Web application context path

gateway_interface

CGI version

http_*

All HTTP headers with http_ prefix

https

Whether request is secure

local_addr

Server IP address

local_host

Server hostname

path_info

Additional path information

path_translated

Physical path of template

query_string

URL query parameters

remote_addr

Client IP address

remote_host

Client hostname

remote_user

Authenticated remote user

request_method

HTTP method (GET, POST, etc.)

request_url

Complete request URL

script_name

Request URI

server_name

Server hostname

server_port

Server port number

server_port_secure

Secure server port

server_protocol

HTTP protocol version

🚫 Client Scope

The client scope is not supported in core BoxLang. This is a CFML legacy scope that is only available via our bx-compat-cfml module. If you would like to use it, please install the module.

Last updated

Was this helpful?