boxlang-language-fundamentals
BoxLang Language Fundamentals
Overview
BoxLang is a modern, dynamic JVM language (JRE 21+) influenced by Java, CFML, Python, Ruby, Go, and PHP. It compiles to Java bytecode and provides complete Java interoperability with a more expressive, concise syntax.
File Types
| Extension | Purpose |
|---|---|
.bx |
Class files (components, services, models) |
.bxs |
Script files (standalone scripts, CLIs) |
.bxm |
Markup/template files (HTML output, views) |
Variables and Scopes
BoxLang uses dynamic typing — types are inferred at runtime. Explicit type annotations are optional but supported.
// Variable declaration (var keyword in functions)
var name = "BoxLang"
var count = 42
var price = 9.99
var active = true
// Null
var nothing = null
Scope Chain (resolution order in a function)
local— variables declared withvarinside a functionarguments— function parametersvariables— component/class private scopethis— component/class public scope- Named scopes:
url,form,session,application,request,cgi,server
// Explicit scope reference
variables.counter = 0
this.publicValue = "visible"
local.tempResult = compute()
Operators
// Arithmetic
a + b a - b a * b a / b a % b a ^ b a \ b // integer divide
// Comparison
a == b a != b a > b a < b a >= b a <= b
a === b a !== b // strict (type + value)
// Logical
a && b a || b !a
a and b a or b not a // word operators
// String concatenation
a & b
// Safe-navigation (null-safe member access)
user?.address?.city // returns null instead of throwing
// Inclusive range (v1.12+)
1..5 // produces [1, 2, 3, 4, 5] — both ends included
// Ternary
result = condition ? trueValue : falseValue
// Elvis (null coalescing)
result = value ?: "default"
Control Flow
// if / else if / else
if ( age >= 18 ) {
writeOutput( "adult" )
} else if ( age >= 13 ) {
writeOutput( "teen" )
} else {
writeOutput( "child" )
}
// switch
switch ( status ) {
case "active":
doActive()
break
case "pending":
case "review":
doPending()
break
default:
doDefault()
}
// for (index)
for ( var i = 1; i <= 10; i++ ) {
writeOutput( i )
}
// for-in (collection)
for ( var item in myArray ) {
writeOutput( item )
}
// for-in with destructuring (v1.12+)
for ( var [key, value] in myStruct ) {
writeOutput( "#key# = #value#" )
}
// while
while ( queue.len() > 0 ) {
process( queue.dequeue() )
}
// do-while
do {
attempt = tryConnect()
} while ( !attempt.success && retries++ < 3 )
Exception Handling
try {
result = riskyOperation()
} catch ( "CustomException" e ) {
handleCustom( e )
} catch ( any e ) {
// e.message, e.detail, e.stackTrace, e.type
logError( e.message )
} finally {
cleanup()
}
// Throwing exceptions
throw( message="Something went wrong", type="MyApp.ValidationError", detail="Field X is required" )
// Or as an object
throw new MyException( "Bad input" )
Strings
// Double-quoted: interpolation enabled
var greeting = "Hello, #name#!"
var multi = "Line one
Line two"
// Single-quoted: literal (no interpolation)
var literal = 'Hello, #name#' // outputs literally: Hello, #name#
// Common string functions
len( str )
trim( str )
uCase( str ) / lCase( str )
left( str, n ) / right( str, n ) / mid( str, start, n )
replace( str, search, replacement )
reFind( pattern, str )
listToArray( str, delimiter )
str.contains( "foo" ) // member function syntax
Arrays
CRITICAL: BoxLang arrays are 1-indexed (not 0-indexed like Java).
The first element is always at index 1.
var fruits = [ "apple", "banana", "cherry" ]
// Access by index — starts at 1
fruits[ 1 ] // "apple" ✅
fruits[ 0 ] // null / out-of-bounds ❌ WRONG
// Preferred: use named accessors instead of numeric index
fruits.first() // "apple" — first element
fruits.last() // "cherry" — last element
// Common array operations
fruits.len() // 3
fruits.append( "date" ) // adds to end
fruits.prepend( "avocado" ) // adds to front
fruits.isEmpty() // false
fruits.contains( "banana" ) // true
fruits.find( "cherry" ) // 3 (1-based index, 0 if not found)
fruits.each( (f) -> println(f) )
fruits.map( (f) -> uCase(f) )
fruits.filter( (f) -> f.startsWith("a") )
fruits.reduce( (acc, f) -> acc & "," & f, "" )
// Inline array literal
var nums = [ 1, 2, 3, 4, 5 ]
// Spread (v1.12+)
var more = [ ...nums, 6, 7 ]
Varargs / Positional Java Calls Must Use Arrays
When calling a Java method that accepts Object... args (varargs), you must
pass a BoxLang array — a bare single value will not work:
// WRONG — bare value
storage.query( "SELECT * FROM users WHERE id = ?", userId )
// CORRECT — wrapped in an array
storage.query( "SELECT * FROM users WHERE id = ?", [userId] )
// Multiple params
storage.query( "SELECT * FROM users WHERE role = ? AND active = ?", [role, active] )
Semicolons
Semicolons are optional at the end of statements and are considered noisy. Do not add them to variable declarations, function calls, return statements, or control-flow blocks.
// BAD — noisy semicolons
var name = "BoxLang";
var total = items.len();
return total;
// GOOD — clean, no semicolons
var name = "BoxLang"
var total = items.len()
return total
Semicolons ARE required (or conventional) in two places:
-
Self-closing component tags — terminate the tag invocation:
bx:header name="Content-Type" value="application/json"; bx:location url="/login" addToken=false; bx:abort; -
Property declarations inside a class:
class MyComponent { bx:property name="title" type="string" default=""; bx:property name="count" type="numeric" default=0; }
Type System
BoxLang is dynamically typed with optional type enforcement:
// Type annotations (optional)
string function greet( required string name ) {
return "Hello, #name#!"
}
// Auto-casting
var num = "42" + 0 // 42 (string auto-cast to number)
var bool = "true" // truthy
var date = "2024-01-15" // auto-cast to date in date functions
// Type checking
isNumeric( val )
isDate( val )
isArray( val )
isStruct( val )
isNull( val )
getMetaData( obj ).name // introspect type
Modern Features (v1.12+)
// Array destructuring
var [first, second, ...rest] = myArray
var [a, b] = [1, 2]
// Struct destructuring
var { name, age } = person
var { name: fullName, age: years } = person // rename
// Spread in function calls
var args = [1, 2, 3]
sum( ...args )
// Spread in array/struct literals
var combined = [...arr1, ...arr2]
var merged = {...struct1, ...struct2}
// For-loop destructuring
for ( var [key, val] in myStruct ) { ... }
for ( var [index, item] in myArray ) { ... }
Comments
// Single-line comment
/* Multi-line
comment */
/**
* Doc-comment (used for annotation metadata)
* @param name The user's name
* @return Greeting string
*/
Built-in Functions (BIFs)
BIFs are globally available without imports. Call them directly or as member functions:
// Function-style
len( myArray )
arrayAppend( myArray, item )
structKeyExists( myStruct, "key" )
// Member-function style (preferred)
myArray.len()
myArray.append( item )
myStruct.keyExists( "key" )
// List all available BIFs
writeDump( getFunctionList() )
Output
writeOutput( "Hello" ) // write to output buffer
println( "Hello" ) // write + newline (scripts)
dump( var=myVar ) // debug dump
writeDump( myVar ) // alias for dump
abort // stop execution
Modern Function Syntax
Method Declaration Without function Keyword
Inside a class body, the function keyword is optional. BoxLang supports a
concise declaration style with optional colon-based return type annotations:
class MathService {
// Concise declaration — no "function" keyword
add( numeric a, numeric b ) {
return a + b
}
// With return-type annotation (colon syntax)
multiply( required numeric a, required numeric b ):numeric {
return a * b
}
// With default parameter value and return type
power( required numeric base, numeric exponent = 2 ):numeric {
return base ^ exponent
}
// Void-like — no return type declared
logOperation( required string operation ) {
writeLog( "Operation: #operation#" )
}
}
Available return types: string, numeric, boolean, array, struct, query,
date, void, any, or a fully-qualified class name.
Traditional function Keyword (Still Valid)
class Service {
function calculate( required numeric value ):numeric {
return value * 2
}
}
Both styles are equivalent. Prefer the concise style inside classes; use the
function keyword for standalone script-level functions and closures.
Quick-Reference: Common Pitfalls
| Pitfall | Wrong | Correct |
|---|---|---|
| Array indexing | arr[ 0 ] |
arr[ 1 ] or arr.first() |
| Trailing semicolons | var x = 1; |
var x = 1 |
| Varargs calls | query( sql, singleVal ) |
query( sql, [singleVal] ) |
| CFML functions in BoxLang | cfheader( ... ) |
bx:header name=... value=...; |
Java-style // not needed |
n/a | Both // and /* */ work |
References
More from ortus-boxlang/skills
boxlang-functional-programming
Use this skill when working with BoxLang lambdas, closures, arrow functions, higher-order functions, functional array/struct pipelines (map, filter, reduce, flatMap, groupBy, etc.), destructuring, or spread syntax.
10boxlang-code-reviewer
Use this skill when reviewing BoxLang code for quality, correctness, security vulnerabilities, performance issues, style violations, or when providing structured code review feedback following BoxLang best practices and security guidelines.
9boxlang-best-practices
Use this skill when writing, reviewing, or improving BoxLang code to ensure it follows community best practices for naming, structure, scoping, error handling, performance, and maintainability.
9boxlang-classes-and-oop
Use this skill when writing BoxLang classes, components, interfaces, inheritance hierarchies, annotations, properties, constructors, or applying object-oriented design patterns in BoxLang.
9boxlang-web-development
Use this skill when building BoxLang web applications: Application.bx lifecycle, request/response handling, sessions, forms, REST APIs, HTTP clients, routing, CSRF protection, Server-Sent Events, or configuring CommandBox/MiniServer.
8boxlang-configuration
Use this skill when configuring BoxLang runtime settings via boxlang.json, setting environment variables for config overrides, configuring datasources, caches, executors, modules, logging, security, or schedulers — or when helping someone understand the BoxLang configuration system.
8