| name | boxlang-core-dev-bif-development |
| description | Use this skill when creating custom BoxLang built-in functions (BIFs): @BoxBIF annotation, invoke() method, argument handling, accessing BoxRuntime and services, BoxLang vs Java BIF implementations, member functions, and registering BIFs via modules. |
BoxLang BIF Development
Overview
Built-in functions (BIFs) are globally available functions in BoxLang — no imports
needed. When placed in a module's bifs/ directory, they are automatically
discovered and registered at module load time. BIFs can be implemented in either
BoxLang or Java.
BoxLang BIF
Minimal Example
// bifs/Greet.bx
// File name = function name (case-insensitive: greet, Greet, GREET all work)
@BoxBIF
class {
function invoke( required string name ) {
return "Hello, #arguments.name#!"
}
}
// Usage anywhere in BoxLang (no import)
greet( "Ada" ) // "Hello, Ada!"
Full-Featured BoxLang BIF
// bifs/StringTitleCase.bx
@BoxBIF
class {
/**
* Converts a string to title case.
*
* @str The input string
* @delimiters Word delimiters (default: space)
* @return Title-cased string
*/
string function invoke(
required string str,
string delimiters = " "
) {
if ( !len( arguments.str ) ) return ""
var words = listToArray( arguments.str, arguments.delimiters )
return words
.map( (w) -> uCase( left(w,1) ) & lCase( mid(w,2,len(w)) ) )
.toList( arguments.delimiters )
}
}
Java BIF
For performance-critical functions or when you need deep JVM access:
package com.example.bifs;
import ortus.boxlang.runtime.bifs.BIF;
import ortus.boxlang.runtime.bifs.BoxBIF;
import ortus.boxlang.runtime.bifs.BoxMember;
import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.scopes.ArgumentsScope;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.types.Argument;
import ortus.boxlang.runtime.types.BoxLangType;
@BoxBIF
@BoxMember( type = BoxLangType.STRING, name = "titleCase" )
public class StringTitleCase extends BIF {
public StringTitleCase() {
super();
declaredArguments = new Argument[] {
new Argument( true, "string", Key.of( "str" ) ),
new Argument( false, "string", Key.of( "delimiters" ), " " )
};
}
@Override
public Object invoke( IBoxContext context, ArgumentsScope arguments ) {
String str = arguments.getAsString( Key.of( "str" ) );
String delimiters = arguments.getAsString( Key.of( "delimiters" ) );
if ( str == null || str.isEmpty() ) return "";
String[] words = str.split( "[" + delimiters + "]" );
StringBuilder sb = new StringBuilder();
for ( int i = 0; i < words.length; i++ ) {
if ( i > 0 ) sb.append( delimiters.charAt( 0 ) );
if ( !words[i].isEmpty() ) {
sb.append( Character.toUpperCase( words[i].charAt(0) ) );
sb.append( words[i].substring(1).toLowerCase() );
}
}
return sb.toString();
}
}
With @BoxMember, the BIF is also callable as a member function:
"hello world".titleCase() // "Hello World"
titleCase( "hello world" ) // "Hello World" (BIF style)
Key Java Classes for BIF Development
| Class/Interface | Package | Purpose |
|---|
BIF | ortus.boxlang.runtime.bifs | Base class for all BIFs |
@BoxBIF | ortus.boxlang.runtime.bifs | Marks a class as a BIF |
@BoxMember | ortus.boxlang.runtime.bifs | Registers as member function |
IBoxContext | ortus.boxlang.runtime.context | Current execution context |
ArgumentsScope | ortus.boxlang.runtime.scopes | Typed argument access |
Key | ortus.boxlang.runtime.scopes | String key interning |
Argument | ortus.boxlang.runtime.types | Argument descriptor |
BoxLangType | ortus.boxlang.runtime.types | Enum of BoxLang types |
BoxRuntime | ortus.boxlang.runtime | Runtime singleton |
Accessing BoxRuntime Services From a BIF
import ortus.boxlang.runtime.BoxRuntime;
import ortus.boxlang.runtime.cache.providers.ICacheProvider;
import ortus.boxlang.runtime.services.CacheService;
@BoxBIF
public class CachedFetch extends BIF {
private BoxRuntime runtime = BoxRuntime.getInstance();
@Override
public Object invoke( IBoxContext context, ArgumentsScope arguments ) {
String url = arguments.getAsString( Key.of( "url" ) );
CacheService cacheService = runtime.getCacheService();
ICacheProvider cache = cacheService.getDefaultCache();
Object cached = cache.get( Key.of( url ) );
if ( cached != null ) return cached;
Object result = fetchFromUrl( url );
cache.set( Key.of( url ), result, 300L, 0L );
return result;
}
}
Accessing Context Services
@Override
public Object invoke( IBoxContext context, ArgumentsScope arguments ) {
IScope requestScope = context.getScopeNearby( RequestScope.name );
IScope appScope = context.getScopeNearby( ApplicationScope.name );
IScope sessionScope = context.getScopeNearby( SessionScope.name );
Object val = context.scopeFindNearby( Key.of( "myVar" ), null ).value();
Object result = context.invokeFunction( Key.of( "len" ), new Object[]{ "hello" } );
return result;
}
BoxLang BIF with Runtime Access
// bifs/GetAppSetting.bx
@BoxBIF
class {
function invoke( required string key, any defaultValue = "" ) {
// Access the BoxRuntime
var runtime = boxRuntime
var appScope = getApplicationScope() // available in web context
return appScope.keyExists( arguments.key )
? appScope[ arguments.key ]
: arguments.defaultValue
}
}
Registering a BIF Alias
@BoxBIF
@BoxBIF( alias = "strTitleCase" )
@BoxBIF( alias = "toTitleCase" )
public class StringTitleCase extends BIF { ... }
Member Function Registration
Member functions are BIFs callable on a specific type using dot notation:
@BoxBIF
@BoxMember( type = BoxLangType.ARRAY, name = "shuffle" )
@BoxMember( type = BoxLangType.STRING, name = "toSlug" )
@BoxMember( type = BoxLangType.STRUCT, name = "deepMerge" )
public class MyBIF extends BIF { ... }
[3,1,2].shuffle()
"Hello World".toSlug() // "hello-world"
config.deepMerge( overrides )
BIF Argument Types
new Argument( required, type, key )
new Argument( required, type, key, defaultValue )
Testing Your BIF
// tests/specs/MyBIFTest.bx
class extends="testbox.system.BaseSpec" {
function run() {
describe( "greet BIF", function() {
it( "should greet a person by name", function() {
expect( greet("Ada") ).toBe( "Hello, Ada!" )
})
it( "should handle empty name", function() {
expect( greet("") ).toBe( "Hello, !" )
})
it( "should be callable as member function", function() {
expect( "Ada".titleCase() ).toBe( "Ada" )
expect( "hello world".titleCase() ).toBe( "Hello World" )
})
})
}
}
box testbox run
Discovering Registered BIFs
// List all registered BIFs (including your custom ones)
writeDump( getFunctionList() )
// Check if a specific BIF exists
if ( structKeyExists( getFunctionList(), "greet" ) ) {
writeOutput( "greet BIF is registered!" )
}
References