13. Built-in Functions
This chapter is the stdlib catalog: conversion, math, strings, files, JSON, dates, and error helpers. How-to for I/O, prompts, jobs, JWT, and UI lives in those chapters; this page keeps signatures and a numbered index.
io.print(math.sqrt(16));
io.print(str.upper("malda"));
io.print(toIntOr("", 1));
13.0 Chapter map
- 13.1 Type conversion
- 13.2 Stdlib namespaces and flat aliases
- 13.3 Mathematical functions
- 13.4 String functions
- 13.5 Environment and host (
webSearch,webFetch) - 13.6 Array utilities
- 13.7 JSON,
validate,evalPrompt— 13.7.2 AI pipeline index - 13.8 File system — 13.9 Git — 13.10 Commands — 13.11 MALDA execution
- 13.12 Spectre.Console — 13.13 Date/time — 13.14 Random — 13.15 Type checks
- 13.16 Encoding — 13.17 Hash — 13.18 Range — 13.19 Errors,
result/option,cap - 13.20–13.24 Indexes into agents, workflows, and UI
- 13.25 Password, JWT, and
req.auth— 13.26 Background jobs — 13.27 Optional packs
13.1 Type Conversion
var num = int("123"); // Convert string to integer
var floatVal = float("3.14"); // Convert string to float
var str = string(42); // Convert number to string
13.1.1 Safe Integer Conversion Helpers
Use these helpers when input may be empty, null, or malformed.
var page = toIntOr(query.page, 1); // Fallback when null/empty/unparseable
var limit = toIntOr(query.limit, 50); // Always returns integer
var ownerId = toIntOrNull(query.ownerId); // Returns null when missing/unparseable
toIntOr(value, fallback)- returns parsed integer when possible, otherwisefallback.toIntOrNull(value)- returns parsed integer ornullwhen value is null/empty/unparseable.- Both functions accept integer, float, boolean, and numeric string values.
13.2 Stdlib Namespaces and Flat Aliases
Most of the standard library is reachable in two spellings: a namespaced call through a module object, and a flat global of the same name. Both resolve to the same implementation and behave identically at runtime.
print(math.sqrt(16)); // namespaced
print(sqrt(16)); // flat alias, same result
print(str.join(str.split("a,b", ","), "|")); // a|b
print(io.readFile("config.json"));
The module objects
| Module | Covers |
|---|---|
math | Rounding, trigonometry, aggregates, random helpers, LLM math, linear algebra, and the constants math.PI, math.E, math.TAU, math.INF, math.NaN |
nn | Neural-net activations, local derivatives, dense / denseBackward, and the loss gradients mseGrad / softmaxGrad. Signatures and the layer model are 14. Neural Nets. No flat aliases |
str | Case, trimming, splitting and joining, padding, regex helpers, similarity, base64, md5 / sha256 |
io | print, input, file and path helpers, glob, grep, environment variables, git helpers |
pdf | extractText — read text from a PDF file (digital text layer; no OCR) |
doc | extractText — read text from a Word .docx (Open XML; not legacy .doc) |
result | ok, err, map, unwrapOr, isOk, isErr — see 13.19.1 |
option | some, none, map, unwrapOr, isSome, isNone — see 13.19.1 |
Math (capital M) is a deprecated alias for the same object as math.
Which flat names are deprecated
Only the flat names that have a math., str., or io. counterpart are considered deprecated aliases. Calling one produces an IDE and language-server warning from source malda-style, in the form Prefer 'io.print(...)' instead of 'print(...)' (deprecated flat alias). The warning is advisory: nothing is removed at runtime and the transpilers accept both spellings.
The affected names, mirroring MaldaLang/BuiltIns/StdLibNamespaces.cs:
- math:
abs,sum,average,max,min,pow,sqrt,floor,ceil,round,trunc,sign,exp,log,log10,log2,sin,cos,tan,asin,acos,atan,atan2,hypot,clamp,degToRad,radToDeg,zeros,rsqrt,randn,argmax,argmin,logSumExp,softmax,dot,matmul,transpose,sigmoid,tanh,randomChoiceWeighted,seed,random,randomInt,randomFloat,anneal - str:
length,upper,lower,trim,text,trimText,substring,indexOf,replace,split,normalizeText,tokenize,tokenOverlap,similarity,extractNumbers,regexMatch,regexReplace,regexFind,startsWith,endsWith,padStart,padEnd,includes,join,repeat,base64Encode,base64Decode,md5,sha256 - io:
print,input,readFile,writeFile,copyFile,readFileBase64,writeFileBase64,readPng,writePng,readTextFileLines,deleteFile,hasFile,hasDirectory,ensureDir,listDirectory,hasEmbeddedFolder,embeddedFolderRoot,glob,grep,replaceInFile,pathExists,pathJoin,pathNormalize,pathGetExtension,isPathUnder,getEnv,getEnvOr,hasEnv,getFileName,getDirectoryName,gitStatus,gitAdd,gitCommit,gitDiff,gitLog,gitBranch,gitCheckout,gitPull,gitPush
Every other built-in in this chapter — sleep, runCommand, parseJson, the agent and workflow helpers, and so on — has no module counterpart and no deprecation attached to it.
Examples/ use the flat spelling (print(...), sqrt(...)) because it is shorter and it is what existing MALDA code looks like. Read those as equivalent to the namespaced form. In new code, prefer the namespaced spelling if you want the editor to stay warning-free.
13.3 Mathematical Functions
Preferred spelling is math.*. Flat names and Math.* are deprecated aliases (see 13.2).
Interpreter, C# transpile, and JavaScript all run math.*. See the backend capability matrix.
print(math.abs(-5));
print(math.pow(2, 3));
| Arguments | Returns | Notes |
|---|---|---|
math.abs(x) | ||
| 1 argument | — | |
math.acos(x) | ||
| 1 argument | — | |
math.anneal(initial, cost, neighbor, options?) | ||
| 3-4 arguments: (initial, cost, neighbor, options?) | { state, cost, steps } | Metropolis search; cost/neighbor are unary lambdas (cooling may be a number or unary lambda); options: steps/temp/cooling/schedule/maximize/copy; uses math.random so math.seed pins it; WF1001 outside a workflow step |
math.argmax(array) | ||
| 1 argument: (array) | — | |
math.argmin(array) | ||
| 1 argument: (array) | — | |
math.asin(x) | ||
| 1 argument | — | |
math.atan(x) | ||
| 1 argument | — | |
math.atan2(a, b) | ||
| 2 arguments | — | |
math.average(array) | ||
| 1 argument: (array) | — | |
math.ceil(x) | ||
| 1 argument | — | returns a float even when whole-valued; integer sinks coerce whole floats; wrap with int() if you need an Integer-tagged value |
math.clamp(x, lo, hi) | ||
| 3 arguments | — | |
math.cos(x) | ||
| 1 argument | — | |
math.degToRad(x) | ||
| 1 argument | — | |
math.dot(a, b) | ||
| 2 arguments: (a, b) | float | 1D vectors, same length |
math.exp(x) | ||
| 1 argument | — | |
math.floor(x) | ||
| 1 argument | — | returns a float even when whole-valued (print shows 1, not 1.0); integer sinks coerce whole floats, so str.repeat("-", math.floor(n)) works; fractional floats still fail |
math.hypot(a, b) | ||
| 2 arguments | — | |
math.log(x) | ||
| 1 argument | — | |
math.log10(x) | ||
| 1 argument | — | |
math.log2(x) | ||
| 1 argument | — | |
math.logSumExp(array) | ||
| 1 argument: (array) | — | |
math.max(a, b) | ||
| 2 arguments or 1 array | — | |
math.matmul(a, b) | ||
| 2 arguments: (a, b) | array | 2D@2D, 2D@1D, or 1D@2D; inner dimensions must match |
math.min(a, b) | ||
| 2 arguments or 1 array | — | |
math.pow(a, b) | ||
| 2 arguments | — | |
math.radToDeg(x) | ||
| 1 argument | — | |
math.randn(std?, mean?) | ||
| 0-2 arguments: (std?, mean?) | — | |
math.random() | ||
| 0 arguments | — | float in [0.0, 1.0) |
math.randomChoiceWeighted(weights) | ||
| 1 argument: (weights) | — | |
math.randomFloat(min, max) | ||
| 2 arguments: (min, max) | — | min included, max excluded |
math.randomInt(min, max) | ||
| 2 arguments: (min, max) | — | min and max are BOTH included, unlike half-open ranges elsewhere; math.seed(n) pins the sequence |
math.round(x) | ||
| 1 argument | — | returns a float even when whole-valued; integer sinks coerce whole floats; wrap with int() if you need an Integer-tagged value |
math.rsqrt(x) | ||
| 1 argument | — | |
math.seed(x) | ||
| 1 argument | null | resets the one generator shared by random, randomInt, randomFloat, randn, randomChoiceWeighted and anneal |
math.sigmoid(x) | ||
| 1 argument: (x) | — | scalar or elementwise 1D/2D; clamps |x|>20 |
math.sign(x) | ||
| 1 argument | — | |
math.sin(x) | ||
| 1 argument | — | |
math.softmax(array, temperature?) | ||
| 1-2 arguments: (array, temperature?) | — | |
math.sqrt(x) | ||
| 1 argument | — | returns a float even when whole-valued (sqrt(4) is 2.0); integer sinks coerce whole floats; wrap with int() if you need an Integer-tagged value |
math.sum(array) | ||
| 1 argument: (array) | — | |
math.tan(x) | ||
| 1 argument | — | |
math.tanh(x) | ||
| 1 argument: (x) | — | scalar or elementwise 1D/2D |
math.transpose(matrix) | ||
| 1 argument: (matrix) | array | 2D nested array |
math.trunc(x) | ||
| 1 argument | — | |
math.zeros(n, cols?) | ||
| 1-2 arguments: (n, cols?) | array | 1D math.zeros(n) or 2D math.zeros(rows, cols); each 2D row is a new array; cells are 0.0 |
math.max and math.min accept (a, b) or a non-empty numeric array. Aggregates are also methods: values.sum(), values.average(), values.min(), values.max(). Trigonometry is in radians; out-of-domain input is NaN, not an error. math.round uses banker's rounding.
math.zeros(n) / math.zeros(rows, cols)
Builds a 1D vector or a 2D nested array filled with 0.0. The two-argument form allocates a fresh row per row, so writing m[0][0] does not change other rows. Dimensions must be integers >= 0. Empty sizes are allowed: math.zeros(0) is [], and math.zeros(2, 0) is two empty rows.
var w = math.zeros(2); // [0, 0]
var g = math.zeros(3, 4); // 3 rows, 4 columns
g[0][0] = 1.0; // other rows stay zero
Neural-net helpers
Linear algebra used by the offline AI_Theory track stays on math. Inner dimensions of math.matmul must match. Activations, local derivatives, a dense layer, and the signature table are 14. Neural Nets.
print(math.dot([1.0, 2.0], [3.0, 1.0]));
13.3.1 LLM-oriented Math Helpers (Reference)
See math.seed through math.anneal in the table. Invalid counts or types, empty arrays, a non-positive softmax temperature, or zero total weight throw. math.seed pins math.random, math.randomInt, math.randomFloat, math.randn, math.randomChoiceWeighted, and math.anneal.
13.3.2 Simulated annealing
math.anneal(initial, cost, neighbor, options?) runs Metropolis simulated annealing. cost and neighbor are unary lambdas (or named functions): cost returns a finite number to minimize, neighbor returns a candidate state. Optional options:
steps— iteration count (default1000)temp/temperature— initial temperature (default1.0)cooling— geometric factor (default0.995) or a unary lambdat => nextTempschedule— same as a functioncoolingmaximize—trueto maximize cost (defaultfalse)copy— clone arrays/dicts before each neighbor call and when saving the best state (defaulttrue)
Returns { state, cost, steps }. Acceptance uses math.random(), so math.seed(n) makes a run reproducible. Like other RNG helpers, anneal is WF1001 outside a workflow step.
math.seed(1);
var result = math.anneal(0.0, x => x * x, x => x, { steps: 5 });
print(result.cost == 0.0);
print(result.steps);
math.seed(7);
var result = math.anneal(
10.0,
x => (x - 3.0) * (x - 3.0),
x => x + math.randomFloat(-1.0, 1.0),
{ steps: 800, temp: 2.0, cooling: t => t * 0.995 }
);
print(result.state);
print(result.cost);
13.4 String Functions
Preferred spelling is str.*. The same names also work as methods on a string value ("hello".upper()). Flat globals are deprecated aliases (see 13.2).
Interpreter, C# transpile, and JavaScript all run str.*. See the backend capability matrix.
print(str.lower("HELLO"));
print(str.startsWith("Hello", "He"));
| Arguments | Returns | Notes |
|---|---|---|
str.base64Decode(x) | ||
| 1 argument | — | |
str.base64Encode(x) | ||
| 1 argument | — | |
str.endsWith(str, suffix) | ||
| 2 arguments: (str, suffix) | — | |
str.extractNumbers(x) | ||
| 1 argument | — | |
str.includes(array, value) | ||
| 2 arguments: (array, value) | — | |
str.indexOf(string, string) | ||
| (string, string) | — | |
str.join(array, separator?) | ||
| 1-2 arguments: (array, separator?) | — | |
str.length(x) | ||
| 1 argument | — | accepts a string or an array |
str.lower(x) | ||
| 1 argument | — | |
str.md5(x) | ||
| 1 argument | — | |
str.normalizeText(string, options?) | ||
| (string, options?) | — | |
str.padEnd(str, length, padChar?) | ||
| 2-3 arguments: (str, length, padChar?) | — | |
str.padStart(str, length, padChar?) | ||
| 2-3 arguments: (str, length, padChar?) | — | |
str.regexFind(string, string) | ||
| (string, string) | — | |
str.regexMatch(string, string) | ||
| (string, string) | — | |
str.regexReplace(string, string, string) | ||
| (string, string, string) | — | |
str.repeat(str, count) | ||
| 2 arguments: (str, count) | — | count must be an integer or a whole-valued float; fractional floats are rejected |
str.replace(string, string, string) | ||
| (string, string, string) | — | |
str.sha256(x) | ||
| 1 argument | — | |
str.similarity(string, string, method?, options?) | ||
| (string, string, method?, options?) | — | |
str.split(string, string?, bool?) | ||
| (string, string?, bool?) | — | |
str.startsWith(str, prefix) | ||
| 2 arguments: (str, prefix) | — | |
str.substring(string, int, int) | ||
| (string, int, int) | — | |
str.text(value) | ||
| 1 argument: (value) | string (never null) | null → empty string (unlike string(null) → "null"); use at env/JSON/CLI boundaries |
str.tokenize(string, options?) | ||
| (string, options?) | — | |
str.tokenOverlap(string, string, options?) | ||
| (string, string, options?) | — | |
str.trim(x) | ||
| 1 argument | — | |
str.trimText(value) | ||
| 1 argument: (value) | string (never null) | null-safe trim: str.trim(str.text(value)) |
str.upper(x) | ||
| 1 argument | — | |
str.substring(text, startIndex, length) — the third argument is a count, not an end index. str.text(value) maps null to "" (unlike string(null) → "null"). str.trimText(value) is str.trim(str.text(value)). Use those at env / JSON / CLI boundaries. str.repeat rejects a fractional count.
Extension-style string methods
The functions length, upper, lower, trim, substring, indexOf, replace, split, startsWith, endsWith, padStart, padEnd, and repeat can also be called as methods on any string value. The receiver is the first argument.
print("hello".upper()); // HELLO
print("HELLO".lower()); // hello
var s = "Hello";
print(s.substring(0, 3)); // Hel
print("abc".repeat(2)); // abcabc
var f = "world".upper; // method as value
print(f()); // WORLD
Calling an unsupported member on a string (e.g. s.foo()) is a runtime error. Path helpers (io.pathJoin, io.isPathUnder, …) are in 13.8. ~/.malda helpers are in 13.5.
13.5 Environment and Host
How-to and the getEnv / str.trim footgun: 12.5 Environment and Host. Host-only (interpreter and C# transpile); JavaScript does not read the process environment or call Brave / webFetch. See the backend capability matrix.
| Arguments | Returns | Notes |
|---|---|---|
getAssistantMemory(path?) | ||
| 0-1 arguments: (path?) | — | |
getCommandLineArgs() | ||
| 0 arguments | — | |
io.getEnv(x) | ||
| 1 argument | string, or null when the variable is not set | unlike io.input (which returns empty string at EOF), missing variables yield null — prefer io.getEnvOr or str.trimText before string sinks |
io.getEnvOr(name, default?) | ||
| 1-2 arguments: (name, default?) | string (never null) | never null; missing variables yield default (empty string when omitted) — preferred before str.trim |
getHostPlatform() | ||
| 0 arguments | — | |
getMaldaConfig() | ||
| 0 arguments | — | |
getMaldaHome() | ||
| 0 arguments | — | |
getProgramDirectory() | ||
| 0 arguments | — | |
getSkillNames() | ||
| 0 arguments | — | |
io.hasEnv(x) | ||
| 1 argument | — | |
httpAuthToken | ||
| request and optional options object | — | |
httpBearerToken(request) | ||
| 1 argument: (request) | — | |
httpCookieToken | ||
| request, cookieName, optional cookieSecret | — | |
loadSkill(name) | ||
| 1 argument: (name) | — | |
loadSkillsFromDir(skillsPath?) | ||
| 0-1 arguments: (skillsPath?) | — | |
webFetch(urlOrToken, pathOrMaxBytes?, maxBytes?, timeoutMs?) | ||
| 1-4 arguments: (urlOrToken, pathOrMaxBytes?, maxBytes?, timeoutMs?) | — | |
webSearch(query, apiKey?) | ||
| at least 1 string argument: (query, apiKey?) | — | |
13.5.1 Host Information
Argument-less built-ins describe the machine and invocation the program is running under. Passing any argument raises an error.
var host = getHostPlatform();
print(host.os); // Operating system identifier
print(host.arch); // Process architecture
print(host.pathSeparator); // "\" on Windows, "/" elsewhere
print(host.description); // Full OS description string
var args = getCommandLineArgs();
// Arguments passed to the program, excluding the program name itself
if (args.length > 0) {
print("first argument: " + args[0]);
}
// Folder of the .malda script (interpreter) or of the compiled .exe (transpile)
var programDir = getProgramDirectory();
var modelPath = io.pathJoin(programDir, "bge-m3-Q4_K_M.gguf");
getHostPlatform() returns an object with the four fields shown above. getCommandLineArgs() returns an array of strings and is the way to read CLI arguments from a MALDA program, in both interpreted and transpiled runs. getProgramDirectory() returns the absolute directory of the running .malda source file under the interpreter, or AppContext.BaseDirectory (the folder containing the compiled executable) after malda compile.
13.5.2 Web search (Brave Search API)
webSearch(query, apiKey?) — Performs a web search using the Brave Search API. query is the search string. apiKey is optional; if omitted, the key is taken from (in order) the apiKey argument, the BRAVE_SEARCH_API_KEY environment variable, or tools.web.search.apiKey in ~/.malda/config.json (see Personal Assistant and CLI). Returns an object: { ok: true, results: [{ title, url, description }, ...], moreResultsAvailable: bool } on success, or { ok: false, error: "message" } on failure.
var r = webSearch("MALDA programming language"); // Uses BRAVE_SEARCH_API_KEY or config
var r2 = webSearch("weather London", "your-brave-api-key"); // Explicit key
if (r.ok) { for (var i = 0; i < r.results.length; i++) print(r.results[i].title); }
13.5.3 HTTP auth token helpers
These helpers extract tokens from request-like objects used by HttpServer handlers.
var bearer = httpBearerToken(req); // Reads Authorization: Bearer <token>
var cookie = httpCookieToken(req, "app_session", "cookie-secret"); // Signed cookie token
var token = httpAuthToken(req, {
"allowBearer": true,
"allowCookie": true,
"allowBody": true,
"cookieName": "app_session",
"cookieSecret": "cookie-secret",
"bodyKey": "auth"
});
httpBearerToken(request)- returns bearer token string or empty string.httpCookieToken(request, cookieName, cookieSecret?)- returns decoded cookie token (or verified secure-cookie value when secret is provided), otherwise empty string.httpAuthToken(request, options?)- tries bearer/cookie/body in order and returns first non-empty token.
Wire these into handlers with req.auth — see 13.25 and 29.6.2.
13.5.4 webFetch(url, maxBytes?, timeoutMs?)
Signature catalog for fetching an http:// or https:// URL on the host path used by httpGet. How-to, caps, and the return shape: 12.9 Fetching URLs. Typed REST calls: 30. REST Web Client. The agent factory is createWebFetchTool.
13.6 Array Utility Functions
math.sum / math.average / math.min / math.max are in 13.3 and also work as array methods. The rows below are the array-only helpers. Interpreter, C# transpile, and JavaScript all run these.
print(str.join(reverse([1, 2, 3, 4, 5]), ", "));
print(includes([1, 2, 3], 3));
| Arguments | Returns | Notes |
|---|---|---|
str.includes(array, value) | ||
| 2 arguments: (array, value) | — | |
str.join(array, separator?) | ||
| 1-2 arguments: (array, separator?) | — | |
reverse(x) | ||
| 1 argument | — | |
sort(array, compareFn?) | ||
| 1-2 arguments: (array, compareFn?) | — | |
toCsv(rows, columns?, options?) | ||
| 1-3 arguments: (rows, columns?, options?) | — | |
13.6.1 CSV Export Helper
var rows = [
{"name": "Alice", "score": 95},
{"name": "Bob", "score": 88}
];
var csv1 = toCsv(rows, ["name", "score"]); // With explicit column order
var csv2 = toCsv(rows); // Infer columns from first row
var csv3 = toCsv(rows, ["name", "score"], {
"delimiter": ";",
"includeHeader": true,
"quoteAll": false,
"newline": "\n"
});
toCsv(rows, columns?, options?) converts an array of rows to CSV text. It escapes quotes and wraps cells when required (delimiter/newlines/quotes). Useful for reports and data export endpoints.
13.7 JSON Functions
Catalog of JSON / schema helpers. toJSON, parseJSON, validate, and asVariant run on interpreter, C# transpile, and JavaScript. evalPrompt is host-only (prompts). See the backend capability matrix.
| Arguments | Returns | Notes |
|---|---|---|
asVariant(typeName, value) | ||
| 2 arguments: (typeName, value) | variant | throws if typeName is not a sum type or the tagged JSON is invalid; already-a-variant is idempotent; validate() does not coerce |
evalPrompt(prompt, fixture, typeName?) | ||
| 2-3 arguments: (prompt, fixture, typeName?) | { ok, data } | { ok: false, error } | no LLM; same extract/validate/coerce path as await; {ok,data} or {ok:false,error}; data is a variant for sum types; instance.eval(fixture) is the same helper |
parseJson(value, schemaRef) and optional options object. For a plain JSON reader use parseJSON(text) | ||
| 2 arguments (value, schemaRef) and optional options object. For a plain JSON reader use parseJSON(text) | — | schema-validating parser for LLM output, NOT a plain JSON reader |
parseJSON(the JSON string). For schema-validated LLM JSON use parseJson(value, schemaRef) | ||
| 1 argument (the JSON string). For schema-validated LLM JSON use parseJson(value, schemaRef) | — | plain JSON text to a value; note that parseJson with a lowercase s is a different built-in |
toJSON(x) | ||
| 1 argument | — | |
validate(schema, value) | ||
| 2 arguments: (schema, value) | — | |
var jsonStr = toJSON(obj); // Convert object to JSON string
var obj = parseJSON('{"name":"Alice","age":30}'); // Parse JSON string to object
toJSON writes dicts, arrays, and the public instance fields of a class (including a primary constructor). Methods and private fields are omitted. A cycle throws. parseJSON always returns a dict — it does not reconstruct the class. Build a new instance yourself if you need methods again.
class Point(x, y);
var d = parseJSON(toJSON(new Point(3, 4)));
print(d.x);
print(d.y);
parseJSON parses arbitrary JSON into MALDA values. parseJson(jsonString, schemaName) parses JSON and validates it against a registered schema declaration, throwing on mismatch.
schema Answer {
text: string;
sources: string[];
}
var parsed = parseJson('{"text":"ok","sources":["a.md"]}', "Answer");
print(parsed.text); // ok
print(parsed.sources[0]); // a.md
Array field types use type[] syntax (e.g. string[], int[], or OtherSchema[]). Field types may also name another declared schema (nested object shapes are expanded for validation and typed prompts) or a declared sum type (tagged oneOf JSON). Optional fields append ? after the type. Unknown field type names and cyclic schema references raise an error when the schema is resolved.
13.7.1 validate(schema, value)
Checks a value that you already have against a schema, without parsing JSON. Unlike parseJson, a validation failure is not an exception: the result is always an object reporting the outcome.
- On success:
{ ok: true, data: value } - On failure:
{ ok: false, error: "message" }
The first argument is either the name of a registered schema or sum type, or an inline schema object. An unknown name raises Unknown schema 'Name'. On success, data is the original value — a tagged dict stays a dict (it is not coerced to a variant). Use asVariant("Intent", dict) or await prompt … -> Intent when you need a variant for match. The agent factory is createValidateJsonTool (no working directory; parallel-safe); it returns the same { ok, data } / { ok: false, error } object.
schema Answer {
text: string;
sources: string[];
}
var candidate = dict { "text": "ok", "sources": ["a.md"] };
var checked = validate("Answer", candidate);
if (checked.ok) {
print(checked.data.text); // ok
} else {
print("invalid: " + checked.error);
}
type Intent = Search(query) | Buy(sku, qty);
var tagged = dict { "tag": "Buy", "sku": "SKU-9", "qty": 2 };
var checkedIntent = validate("Intent", tagged);
if (checkedIntent.ok) {
print(checkedIntent.data.tag);
}
type Intent = Search(query: string) | Buy(sku: string, qty: int);
var tagged = dict { "tag": "Buy", "sku": "SKU-9", "qty": "x" };
var checkedTyped = validate("Intent", tagged);
print(checkedTyped.ok);
memory.validate() on a GraphMemory instance, which checks graph integrity rather than a schema. The two share a name but are unrelated.
13.7.1.1 asVariant(typeName, value)
Turns a tagged dict into a sum-type variant so match can bind constructors. validate("Intent", dict) never does this. An already-built variant is returned as-is when its tag belongs to the type. Mismatch (unknown type name, object schema, bad tag, typed payload that fails JSON Schema) throws — unlike validate.
type Intent = Search(query: string) | Buy(sku: string, qty: int) | Help();
var tagged = dict { "tag": "Buy", "sku": "SKU-9", "qty": 2 };
var checked = validate("Intent", tagged);
var label = match asVariant("Intent", checked.data) {
case Search(q): "search " + q;
case Buy(sku, qty): "buy " + sku + " x " + qty;
case Help(): "help";
};
print(label);
See Examples/Basics/as_variant.malda and docs/llm/few-shot/25_as_variant.malda.
13.7.1.2 evalPrompt(prompt, fixture, typeName?)
Offline fixture in/out on a PromptInstance. Does not call an LLM, consume @budget, or run a gather tool round. The fixture is a dict/object or an LLM-shaped JSON string (markdown fences and a surrounding { … } extract the same way await does). The result shape matches validate:
- On success:
{ ok: true, data }—datais whatawait prompt … -> Typewould have returned (object for a schema, variant for a sum type, program value forprogram(Api)). - On extract / parse / schema failure:
{ ok: false, error }. There is no repair loop.
The type comes from the instance’s declared -> Type (instance.returnType), including Mode C gather: instances that have not attached response_format yet. Pass optional typeName to override. instance.eval(fixture, typeName?) is the same helper. Pipe-friendly: p |> evalPrompt(fixture).
schema Card {
name: string;
}
prompt extract(raw) -> Card {
user: raw;
}
var p = extract("Ada Lovelace");
var checked = evalPrompt(p, dict { "name": "Ada" });
if (checked.ok) {
print(checked.data.name);
}
type Intent = Search(query: string) | Buy(sku: string, qty: int) | Help();
prompt parseUtterance(text) -> Intent {
user: text;
}
var checked = evalPrompt(parseUtterance("buy"), dict { "tag": "Buy", "sku": "SKU-9", "qty": 2 });
var label = match checked.data {
case Search(q): "search " + q;
case Buy(sku, qty): "buy " + sku + " x " + qty;
case Help(): "help";
};
print(label);
See Examples/Prompts/eval_prompt.malda and docs/llm/few-shot/27_eval_prompt.malda.
13.7.2 AI Pipeline Functions
How-to, worked pipes, and the signature table live in 10.9.1 AI pipeline helpers. Vector index + asRetriever: 16. VectorDB. Offline fixtures: 13.7.1.2 evalPrompt.
Catalog names: loadDocuments, splitDocuments, indexInto, formatRetrievedDocs, composePipe, parallelRun, mergeRetrievedDocs, withExamples, evalPrompt, runPrompt, parseJson.
13.8 File System Functions
How-to, failure styles, line ranges, capability tokens, and backend notes: 12. Input/Output. This section is the signature catalog. File I/O is host-only (interpreter and C# transpile); JavaScript has io.print / io.input / JSON, not disk files. See the backend capability matrix.
| Arguments | Returns | Notes |
|---|---|---|
io.copyFile(src, dest) | ||
| 2 arguments: (src, dest) | boolean | binary-safe overwrite; returns false on I/O failure; rejects embed: src and dest; does not create parent directories |
io.deleteFile(x) | ||
| 1 argument | — | |
editFile(string, array) | ||
| (string, array) | — | |
io.embeddedFolderRoot(alias) | ||
| 1 argument: (alias) | — | |
io.ensureDir(x) | ||
| 1 argument | — | |
extractDocxText(path) | ||
| 1 argument: (path) | — | |
extractPdfText(path, password?) | ||
| 1-2 arguments: (path, password?) | — | |
io.getDirectoryName(x) | ||
| 1 argument | — | |
io.getFileName(x) | ||
| 1 argument | — | |
io.glob(pattern, dirPath?, ...) | ||
| at least 1 argument: (pattern, dirPath?, ...) | — | |
io.grep(pattern, filePath, ...) | ||
| at least 2 arguments: (pattern, filePath, ...) | — | |
io.hasDirectory(x) | ||
| 1 argument | — | |
io.hasEmbeddedFolder(alias) | ||
| 1 argument: (alias) | — | |
io.hasFile(x) | ||
| 1 argument | — | |
insertAtLine(filePath, lineNumber, content, ...) | ||
| at least 3 arguments: (filePath, lineNumber, content, ...) | — | |
io.isPathUnder(root, path) | ||
| 2 arguments: (root, path) | — | |
io.listDirectory(x) | ||
| 1 argument | — | |
io.pathExists(x) | ||
| 1 argument | — | |
io.pathGetExtension(x) | ||
| 1 argument | — | |
io.pathJoin | ||
| at least 1 argument | — | |
io.pathNormalize(x) | ||
| 1 argument | — | |
io.readFile(string, int?, int?) | ||
| (string, int?, int?) | — | |
io.readFileBase64(x) | ||
| 1 argument | — | |
io.readTextFileLines(x) | ||
| 1 argument | — | |
io.replaceInFile(string, string, string, int?) | ||
| (string, string, string, int?) | — | |
io.writeFile(a, b) | ||
| 2 arguments | — | |
io.writeFileBase64(path, base64Content) | ||
| (path, base64Content) | — | |
io.writePng(path, width, height, pixels) | ||
| (path, width, height, pixels) | — | |
io.readPng(path) | ||
| 1 argument | — | |
13.8.0 Embedded folders (embed:)
Compile with malda compile … --embed-folder <dir[=alias]> to pack a directory into the executable as assembly resources. At runtime, read those files with the virtual scheme embed:<alias>/<relative> — nothing is extracted to disk. Writes (writeFile, ensureDir, …) reject embed: paths.
if (io.hasEmbeddedFolder("secondbrain")) {
var root = io.embeddedFolderRoot("secondbrain"); // "embed:secondbrain"
var catalog = io.readFile(io.pathJoin(root, "brain.json"));
var entries = io.listDirectory(root);
}
hasEmbeddedFolder(alias) returns whether that alias was embedded. embeddedFolderRoot(alias) returns embed:<alias>, or null when the alias is missing. Agent tools such as createReadFileTool, createGrepTool, and createListDirectoryTool accept an embed:<alias> working directory.
replaceInFile("file.txt", "old", "new", 3); // Replace text in file (single replacement)
var edits = [{"oldText": "old1", "newText": "new1"}, {"oldText": "old2", "newText": "new2"}];
var result = editFile("file.txt", edits); // Apply multiple edits to file
13.8.1 Line, Binary, Delete, and PDF Text
| Returns | Behavior |
|---|---|
pdf.extractText(path, password?) | |
| string | Extracts text from a PDF using PdfPig (ContentOrderTextExtractor). Optional password for encrypted files. Throws when the path is missing or the PDF cannot be opened. Digital text only — scanned/image PDFs need OCR elsewhere. Prefer the namespaced spelling; the CallBuiltIn name is extractPdfText. |
doc.extractText(path) | |
| string | Extracts body paragraph text from a Word .docx using the Open XML SDK. Throws when the path is missing, the file is not .docx, or the package cannot be opened. Legacy binary .doc is not supported. Prefer the namespaced spelling; the CallBuiltIn name is extractDocxText. |
readTextFileLines(path) | |
array of strings, or null | Reads the file as UTF-8 and splits it into lines. Returns null when the file is missing or cannot be read. |
deleteFile(path) | |
| boolean | Deletes the file. Returns true when the file was deleted or was already absent; false on an empty path or an I/O failure. |
copyFile(src, dest) | |
| boolean | Copies the file as bytes (overwrites dest). Returns false on I/O failure. Rejects embed: source and dest. Does not create parent directories. Prefer io.copyFile. |
insertAtLine(path, lineNumber, content, insertAfter?) | |
| boolean | Inserts content (which may span several lines) at a 1-indexed line. Returns false if the file does not exist. |
readFileBase64(path) | |
string, or null | Reads the file as bytes and returns base64. Returns null when the file is missing. |
writeFileBase64(path, base64Content) | |
| boolean | Decodes base64 and writes the bytes. Returns false when the input is not valid base64 or the write fails. |
io.writePng(path, width, height, pixels) | |
| boolean | Writes an 8-bit RGBA PNG. pixels is a flat array of length width * height * 4 (R, G, B, A). Channels are rounded to the nearest integer and clamped to 0–255. Returns false on I/O failure. Rejects embed: paths. Throws when the size is not positive, the image is larger than 16 megapixels, the array length does not match, or a channel is not a number. |
io.readPng(path) | |
object, or null | Reads an 8-bit non-interlaced PNG (grayscale, gray+alpha, RGB, or RGBA) and returns { width, height, pixels } with a flat RGBA array. Returns null when the file is missing or is not a decodable PNG of that kind. Indexed, 16-bit, and interlaced images return null. |
var lines = readTextFileLines("notes.txt");
if (lines != null) {
print("line count: " + lines.length);
}
insertAtLine("notes.txt", 1, "# Header"); // Insert before line 1
insertAtLine("notes.txt", 3, "appended", true); // Insert after line 3
var encoded = readFileBase64("logo.png");
writeFileBase64("logo-copy.png", encoded);
var px = [255, 0, 0, 255];
io.writePng("pixel.png", 1, 1, px);
var image = io.readPng("pixel.png");
deleteFile("scratch.txt");
var pdfText = pdf.extractText("manual.pdf");
// var unlocked = pdf.extractText("secret.pdf", "password");
var docText = doc.extractText("brief.docx");
For insertAtLine, a lineNumber of 0 or less inserts at the start of the file, and a value beyond the last line appends to the end. insertAfter defaults to false, meaning the content is inserted before the given line.
readTextFileLines, deleteFile, insertAtLine, the base64 helpers, and io.readPng / io.writePng report ordinary failures through their return value (false or null) rather than by throwing. They only raise an error when called with the wrong number of arguments or the wrong argument types (io.writePng also raises when the pixel buffer does not match width and height). Check the result rather than relying on try/catch. pdf.extractText and doc.extractText throw on missing files and parse failures.
insertAtLine is interpreter-only: it is not available in transpiled executables. The other file helpers, io.writePng / io.readPng, pdf.extractText / extractPdfText, and doc.extractText / extractDocxText are supported by the C# transpiler. The JavaScript backend does not implement io.writePng or io.readPng.
13.8.2 Searching Files: glob and grep
glob finds paths by pattern, and grep searches file contents. Both take a required argument first and then a series of optional, positional arguments.
glob(pattern, dirPath?, maxResults?, includeDirectories?, excludeDirs?, workingDirectory?)
dirPathdefaults to".",maxResultsto200(capped at 500),includeDirectoriestofalse, and the two remaining string arguments to"".- On success it returns an object
{ items, count, truncated }, where each item is{ name, type, path }andtruncatedreports whether the result hitmaxResults. - Recursive
globandgrepdo not follow directory junctions or symbolic links. The link itself can still appear in a one-levellistDirectoryresult.
var found = glob("**/*.malda", "Examples");
print(found.count);
print(found.truncated);
var first = found.items[0];
print(first.path + " (" + first.type + ")");
grep(pattern, filePath, useRegex?, caseInsensitive?, includeLineNumbers?, contextLines?, countOnly?, recursive?, workingDirectory?)
- Defaults:
useRegexfalse,caseInsensitivefalse,includeLineNumberstrue,contextLines3,countOnlyfalse,recursivetrue. - Normally returns an array of matches shaped
{ filePath, content, lineNumber, contextBefore, contextAfter }. WithcountOnlyset it returns{ count, filesSearched }instead. - Files that cannot be read are skipped silently.
var matches = grep("function ", "Examples");
for (var m in matches) {
print(m.filePath + ":" + m.lineNumber + " " + m.content);
}
var summary = grep("TODO", "Examples", false, true, true, 0, true);
print(summary.count + " hits in " + summary.filesSearched + " files");
glob and grep return an error message instead of their normal result. Guard with typeOf(result) == "string" before treating the value as a collection.
var result = grep("needle", "does-not-exist");
if (typeOf(result) == "string") {
print("search failed: " + result);
}
13.9 Git Functions
var status = gitStatus("."); // Get repository status
gitAdd(".", "."); // Stage files
gitCommit(".", "Fix bug"); // Create commit
var commits = gitLog(".", 10); // Get last 10 commits
var diff = gitDiff(".", null, false); // Get unstaged diff
var branches = gitBranch(".", "list"); // List all branches
gitBranch(".", "create", "feature-x"); // Create new branch
// Note: Branch deletion is disabled for safety
gitCheckout(".", "main", false); // Switch to branch
gitPush(".", "origin", "main"); // Push to remote
gitPull(".", "origin", "main"); // Pull from remote
13.10 Command Execution Functions
// Execute shell commands
var result = runCommand("dotnet", ["build"], ".", 30000); // Run dotnet build with 30s timeout
// Returns: {exitCode: 0, stdout: "...", stderr: ""}
var result = runCommand("npm", ["test"]); // Run npm test (no timeout)
var result = runCommand("python", ["script.py"], "./scripts"); // Run with custom working directory
// Result object structure:
// - exitCode (integer): Process exit code (0 typically indicates success)
// - stdout (string): Standard output from the command
// - stderr (string): Standard error output from the command
13.11 MALDA Execution Functions
// Execute MALDA code from file or string
var result = runMALDA("example.malda"); // Execute from file
var result = runMALDA("print('Hello');"); // Execute from string
var result = runMALDA("example.malda", "input data"); // Execute with stdin input
// Result object structure:
// - success (boolean): true if execution completed without errors
// - output (string): Standard output from the program
// - error (string): Parse error message if any, empty string otherwise
// - runtimeError (string, optional): Runtime error message if any
// Compile a .malda program to an executable (optional embed folder = CLI --embed-folder)
var packed = compileMALDA("app.malda", "dist/app.exe", "transpile", "secondbrain");
// embedFolder may be "path" or "path=alias"; result: { success, outputPath, error, errors }
// mode is interpreter (default), transpile (C#), or js (browser). game.*/dom.*/three.* need js.
var gameJs = compileMALDA("game.malda", "game.js", "js");
13.11.1 getSymbols(sourceOrFilePath)
Parses MALDA code and extracts structured symbol information (classes, functions, actors, prompts) with line numbers and signatures. Accepts one string argument: a file path to a .malda file or MALDA source code. If the string looks like a file path (contains path separators or ends with .malda) and the file exists, the file is read and its content is parsed; otherwise the argument is treated as source code. Path traversal (.., ~) is rejected. Returns an object with classes, functions, actors, prompts, imports, and parseErrors (all arrays). Each class/actor has name, line, column, members; each function has name, line, column, parameters, signature; each prompt has name, line, column, parameters, returnType?, signature. Each import has file?, resolvedPath?, package?, alias?; unresolved modules are not flagged here (use checkMalda, whose malda-import warning names the missing path). Parse errors have line, column, message.
var sym = getSymbols("Examples/OOP/classes_objects.malda"); // From file
var sym2 = getSymbols("function f() { } class C { }"); // From source
// sym.classes, sym.functions, sym.actors, sym.prompts, sym.imports, sym.parseErrors
13.11.2 getParseErrors(sourceOrFilePath)
Parses MALDA code and returns only parse errors (no execution). Accepts the same argument as getSymbols: file path or source string. Path resolution and security checks are the same. Returns { parseErrors: [{ line, column, message }, ...] }. Use to validate syntax without running or compiling.
var errs = getParseErrors("var x = ;"); // Invalid: empty rhs
// errs.parseErrors is non-empty with line, column, message
var ok = getParseErrors("var x = 1;"); // Valid: parseErrors is []
13.11.3 checkMalda(sourceOrFilePath, typeMode?, workingDir?)
Diagnoses MALDA code with the same LanguageService diagnostics as malda check --json (parser, types, schema/sum-type names, interpolation). Does not execute the program. Accepts a file path or inline source (same path heuristic as getParseErrors). Optional typeMode is default (IDE: type mismatches are errors), strict (full CLI suite), or lenient (type mismatches as warnings). Returns { ok, executed: false, file, errorCount, warningCount, infoCount, error?, diagnostics }. Each diagnostic has severity, code, message, file?, line, column (1-based), length, hint?, suggestedFix?. The agent factory is createCheckMaldaTool.
var report = checkMalda("var x = 1;");
print(report.ok); // true
var typed = checkMalda("var n: int = \"abc\";", "lenient");
print(typed.ok); // true (mismatch is a warning)
runCommand function includes comprehensive safety checks:
- Dangerous commands are blocked (e.g.,
rm,del,format,shutdown,powershell,cmd, etc.) - Working directory is validated and normalized to prevent path traversal
- Maximum timeout limit of 1 hour (3,600,000ms) to prevent indefinite waits
- Command paths are validated for absolute paths
- Path traversal attempts (
..,~) are detected and blocked
13.12 Spectre.Console Integration
MALDA exposes a global AnsiConsole object that wraps the .NET Spectre.Console library and provides rich console output:
// Markup (colors, bold, links, etc.)
AnsiConsole.markupLine("[green]Hello[/] [bold red]Spectre.Console[/]!"); // Appends a newline
AnsiConsole.markup("[dim]no newline, [/]"); // Stays on the same line
AnsiConsole.markupLine("[cyan]rest of the line[/]");
// Tables
var rows = [
{"Name": "Alice", "Score": 95},
{"Name": "Bob", "Score": 88}
];
AnsiConsole.table(rows, "Scores");
// Panels (body and title accept markup)
AnsiConsole.panel("[bold]Important[/] message", "Info", "rounded");
// Trees
var treeData = {
"root": "Project",
"children": [
{"label": "src"},
{"label": "tests"}
]
};
AnsiConsole.tree("Project", treeData);
// Status
AnsiConsole.status("Processing...", () => {
// Long-running work here
});
// Prompt (configuration object)
AnsiConsole.prompt({
"type": "text",
"message": "Enter your name"
});
// Progress - New callback-based syntax (recommended)
AnsiConsole.progress((ctx) => {
ctx.addTask("Loading World Map", 100);
while (!ctx.isFinished()) {
ctx.increment("Loading World Map", 1.5);
sleep(50);
}
});
// Progress - Object-based syntax (legacy)
AnsiConsole.progress({
"tasks": [
{"name": "Task 1", "maxValue": 100}
],
"action": (progressObj) => {
var current = 0;
while (current < 100) {
current = current + 10;
// Update progress by setting the value
progressObj["Task 1"] = current;
sleep(50);
}
}
});
AnsiConsole.markupLine(text)writes the text and appends a newline. It is the right call for a standalone line of output.AnsiConsole.markup(text)writes the text with no trailing newline, so consecutive calls run together on one line. Use it to compose a line from several styled fragments, then close the line withmarkupLine.- Both parse markup tags such as
[bold],[green]and[/]. To print a literal bracket, double it:[[and]]. AnsiConsole.panel(body, title?, border?)parses markup in both the body and the title. Content that is not valid markup — arbitrary JSON, code, or error text containing brackets — is rendered literally instead of raising an error.
- Callback-based syntax: Pass a function that receives a context object with methods:
ctx.addTask(name, maxValue)- Add a progress taskctx.increment(taskName, value)- Increment a task's progress by the given valuectx.isFinished()- Check if all tasks are finished
- Object-based syntax: Pass an object with a
tasksarray and anactioncallback. The callback receives a progress object where you can read and update progress values by setting properties:progressObj["Task Name"] = value
- Command-line interpreter: When you run
maldain a real terminal, MALDA configures UTF-8 and enables ANSI escape sequences on supported Windows consoles so Spectre.Console markup renders with full colors and styling. - Transpiled executables: Generated C# executables enable ANSI escape sequences at startup on Windows, so
AnsiConsole.*calls render correctly in compatible terminals. - Desktop IDE: The IDE output panel is plain text. On first use of
AnsiConsole, MALDA automatically opens a separate console window and routes output to both the IDE panel (plain text) and the console (rich Spectre.Console rendering).
13.13 Date and Time Functions
var timestamp = now(); // Get current timestamp in milliseconds (Unix epoch)
var formatted = formatDate(timestamp, "yyyy-MM-dd HH:mm:ss"); // Format timestamp (default: "yyyy-MM-dd HH:mm:ss")
var parsed = parseDate("2026-01-26 10:30:00"); // Parse date string to timestamp
var tomorrow = addDays(timestamp, 1); // Add days to timestamp
var inHours = addHours(timestamp, 2); // Add hours to timestamp
- Timestamps are in milliseconds since Unix epoch (January 1, 1970 UTC)
formatDateuses standard .NET date format strings (e.g., "yyyy-MM-dd", "HH:mm:ss", "dd/MM/yyyy")parseDateaccepts common date formats and converts to timestamp
13.14 Random Number Functions
var rand = math.random(); // Random float between 0.0 and 1.0
var randInt = math.randomInt(1, 10); // Random integer, 1 and 10 both included
var randFloat = math.randomFloat(0.0, 100.0); // Random float between 0.0 and 100.0
math.seed(7); // Pin the sequence for reproducible runs
var pinned = math.randomInt(1, 100); // Always 39 after seed(7)
randomInt(min, max)includes both endpoints, unlike the half-open ranges common in other languages.math.seed(n)resets the shared generator behindrandom,randomInt,randomFloat,randn,randomChoiceWeightedandanneal. Seed at the top of a program to make a run deterministic, which is what makes a random program testable.- The flat spellings
randomInt(1, 10)andMath.randomInt(1, 10)still run, but the language server reports both as deprecated aliases. Prefermath., as described in 13.2 Stdlib Namespaces and Flat Aliases.
13.15 Type Checking Functions
var isNum = isNumber(42); // Check if value is a number (integer or float)
var isStr = isString("hello"); // Check if value is a string
var isArr = isArray([1, 2, 3]); // Check if value is an array
var isObj = isObject({name: "Alice"}); // Check if value is an object
var type = typeOf(42); // Canonical tag ("int", "string", "dict", "object", "variant", "task", "null", ...)
var isInt = isTag(42, "int"); // true; isTag also accepts legacy names like "integer" during deprecation
13.16 Encoding and Decoding Functions
var encoded = base64Encode("Hello World"); // Base64 encode string
var decoded = base64Decode(encoded); // Base64 decode string
var urlEncoded = urlEncode("Hello World"); // URL encode string (returns "Hello%20World")
var urlDecoded = urlDecode(urlEncoded); // URL decode string
13.17 Hash Functions
var md5Hash = md5("Hello World"); // Calculate MD5 hash (returns hex string)
var sha256Hash = sha256("Hello World"); // Calculate SHA-256 hash (returns hex string)
- Both
md5andsha256return lowercase hexadecimal strings - MD5 produces 32-character hex strings
- SHA-256 produces 64-character hex strings
Password hashes, JWT, CSRF, and signed cookies are not digest helpers — see 13.25.
13.18 Range Generation
var range1 = range(5); // Generate [0, 1, 2, 3, 4]
var range2 = range(1, 10); // Generate [1, 2, 3, 4, 5, 6, 7, 8, 9]
var range3 = range(0, 10, 2); // Generate [0, 2, 4, 6, 8] (step of 2)
var range4 = range(10, 0, -1); // Generate [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] (negative step)
13.19 Error Handling Functions
exit(0); // Exit program with exit code 0 (success)
exit(1); // Exit program with exit code 1 (error)
error("Something went wrong"); // Throw runtime error with message
assert(x > 0, "x must be positive"); // Assert condition is true, throw error if false
assert(isValid, "Validation failed"); // Assert without custom message
exit(code)immediately terminates the program with the specified exit code (0 = success, non-zero = error)error(message)throws a runtime error that can be caught by error handling mechanismsassert(condition, message?)is useful for debugging and validation - throws an error if condition is false
13.19.1 result and option
Prefer these modules when a function should report success or absence without throwing. They return variants with tags Ok/Err and Some/None — the same shape as a type Result = Ok(value) | Err(message) declaration, but you do not need to declare the type. The module names are lowercase (result, option); a user-declared type Result is a different name.
Use try/catch for unexpected failures (see 8. Control Structures). Use result/option for expected outcomes in the return value.
function parseAge(raw) {
var n = toIntOrNull(raw);
if (n == null) {
return result.err("not an int");
}
return result.ok(n);
}
print(result.unwrapOr(parseAge("12"), 0));
print(result.isErr(parseAge("x")));
var present = option.some("hi");
var missing = option.none();
print(option.unwrapOr(present, "no"));
print(option.isNone(missing));
result.ok(value)/result.err(value)— construct anOkorErrvariantresult.isOk(r)/result.isErr(r)— tag testsresult.unwrapOr(r, fallback)— payload ofOk, otherwisefallbackresult.map(r, fn)— applyfnto anOkpayload; leaveErrunchangedresult.andThen(r, fn)— ifOk, callfn(payload)and return that Result; ifErr, skipfn.fnmust returnOk/Err(usemapto transform a bare payload)option.some(value)/option.none()— construct aSomeorNonevariantoption.isSome(o)/option.isNone(o)/option.unwrapOr(o, fallback)/option.map(o, fn)/option.andThen(o, fn)— the same pattern for optional values
function parseAge(raw) {
var n = toIntOrNull(raw);
if (n == null) {
return result.err("not an int");
}
return result.ok(n);
}
function doubleAge(n) {
return result.ok(n * 2);
}
print(result.unwrapOr(parseAge("12") |> result.andThen(doubleAge), 0));
print(result.isErr(parseAge("x") |> result.andThen(doubleAge)));
Do not treat type hints (var n: int = …) as runtime validation. For JSON-shaped payloads use schema and validate(). Tutorial: errors, Result/Option, and schema validate.
13.19.2 grounded.wrap
Retrieval citations (GraphMemory, files, tools) are not a match-visible value kind. Wrap a payload so provenance is a dict you can read:
var g = grounded.wrap("the sky is blue", [
{ "source": "wiki", "id": "p1", "span": "12-40" }
]);
print(g.value);
print(g.sourced);
print(g.citations[0].source);
grounded.wrap(value, citations?)— returns{ value, citations, sourced }.sourcedis true when the citation list is non-empty after normalization.- Each citation is
{ source, id?, span? }. A string citation becomes{ source }. There is no flatgrounded()alias. - GraphMemory opt-in ASK:
memory.ask(query, maxResults?, options?)(orquery(..., { grounded: true })) wraps hits with citations fromfilePath/source/nodeId. Plainquery()still returns an array. See 20. GraphMemory §20.3.15.
13.19.3 cap.fileRead
This subsection is the signature catalog. If you have not used tokens before, start with the how-to in 12.6 Capability tokens — why they exist, the key analogy, cap.confine, and the host-mint pattern for tools.
A token is a sealed host object with kind and path (and name for an MCP tool). JSON and object literals cannot rehydrate one. @effects("io") only names the effect; it does not bind a path, URL, tool, or argv.
var notes = cap.fileRead("notes.md");
print(notes.kind);
print(cap.is(notes, "fileRead"));
var forged = false;
try {
cap.read({ "kind": "fileRead", "path": "notes.md" });
} catch (e) {
forged = true;
}
print(forged);
cap.fileRead(path)/cap.fileWrite(path)/cap.dirList(path)/cap.httpGet(origin)/cap.mcpCall(server, tool?)/cap.shell(prefix)— mint a token. There is no flatcap()alias.cap.read(token, startLine?, endLine?)/cap.write(token, content)/cap.list(token)/cap.fetch(token, urlOrPath?, maxBytes?, timeoutMs?)/cap.invoke(token, server, args?)/cap.run(token, extraArgs?, workingDir?, timeout?)— consume a matching kind only. Strings and{ kind, path }dicts throw. Wrong-origin / wrong-tool /../ absolute extras throw before HTTP, MCP, or process start.cap.is(value, kind?)— true only for a real token. Omitkindto accept any token kind.cap.confine(token, relative)— same kind, narrower path / URL prefix / MCP tool / argv prefix; values outside the parent throw.io.readFile/io.writeFile/io.listDirectory/webFetch/callTool/runCommandalso accept a matching token. Ordinary strings still work for non-tool code.createWebFetchTool(httpCap?)/createRunCommandTool(workingDir?, shellCap?)bind a token so model-supplied URLs / commands stay under it.- Mark handlers that mint or consume tokens
@effects("cap")(or list both"io"and"cap"). JavaScript can mint /is/confine; consume is host-only. - Do not pass a token through LLM JSON. Host-mint a workspace root and
cap.confinea relative path / URL / tool / extra argv from tool args. Scaffold:malda new agent. - Examples:
Examples/Tools/capability_tokens.malda,Examples/Tools/capability_http_mcp_shell.malda.
13.19.4 expect
Throws like assert when the condition is false. Used in suite cases; malda eval runs those cases. validate still returns { ok, error }. See 24.1.
expect(checked.ok);
expect(n > 0, "n must be positive");
13.19.5 stream
Yields an array of { text } chunks, or schema-aware partials when a registered schema name is passed. LlamaCpp is buffered, not live tokens. Iterate with for await. See 24.8.
var chunks = stream("hello world from malda");
13.19.6 race / firstOk
race is the first completed branch. firstOk is the first branch that is not a Timeout variant. all waits for every branch and puts AgentError.Upstream in failed slots. See 24.4.
race(taskA, taskB);
firstOk(taskA, taskB);
13.19.7 trace.span / trace.journal / trace.lastUsage
In-process run journal: prompt, tool, cap, hop, and workflow-step events. .usage on a prompt result is metadata (own key wins). See 24.3.
trace.span("name", () => { });
var events = trace.journal();
var spend = trace.lastUsage();
13.19.8 AgentError
Built-in sum type attached as agentError beside the string error. Throws still throw. validate mismatch → SchemaMismatch. See 24.7.
AgentError = Refused(reason) | Unparsable(raw, attempts) | SchemaMismatch(schema, error)
| BudgetExceeded(kind, limit) | ToolDenied(tool, reason) | Timeout(ms)
| Upstream(status, body)
13.20 Structured Task Planning
decomposeTask, executePlan, createSubmitPlanTool, createUpdatePlanTool, and createMarkStepTool live in 19. Agent Orchestration (§ 19.13). agents.define / agents.team bind role specs to a directed graph; executePlan(plan, team) honors step.role and declared hops, branches reject hops on the predecessor approved verdict, and accepts plan.think: false to stay offline — see § 19.17. team.handoff / team.review / team.reject / team.consult are typed hops and stay validate-only unless think is true. runProgram runs a closed api / program(ApiName) value with no further LLM calls — see 10. Prompts (§ 10.8 Closed APIs and Deterministic Programs).
13.21 Tool Creation Functions
Agent tool factories (createReadFileTool, createWriteFileTool, createReplaceInFileTool, createEditFileTool, createInsertAtLineTool, createGrepTool, createListDirectoryTool, createAskUserTool, createWebSearchTool, createWebFetchTool, createGitStatusTool, createGitAddTool, createGitCommitTool, createGitLogTool, createGitDiffTool, createGitBranchTool, createGitCheckoutTool, createGitPushTool, createGitPullTool, createRunCommandTool, createRunMALDATool, createCompileMALDATool, createGetSymbolsTool, createGetParseErrorsTool, createCheckMaldaTool, createValidateJsonTool, createTestMaldaTool, createSubmitPlanTool, createUpdatePlanTool, createMarkStepTool, createGlobTool, createCreateMcpAgentScriptTool, createDeleteFileTool, createCopyFileTool, createEnsureDirTool) live in 19. Agent Orchestration (§ 19.6). createNativeCallback wraps a MALDA function as a .NET delegate — see 32. .NET Interop.
13.22 Durable Workflow Operations
Runtime, query, and control helpers (startWorkflow, getWorkflowStatus, getWorkflow, getWorkflowSteps, getWorkflowEvents, getWorkflowMetrics, listWorkflows, listWorkflowDeadLetters, requeueDeadLetter, runWorkflowInstance) live in 23. Durable Workflows.
13.23 Agent Runtime Logging
Diagnostic helpers (enableAgentVerboseLogging, setAgentVerbosePhase, setAgentStatusBanner, reportRalphStatus) live in 19. Agent Orchestration (§ 19.5 and 19.19 Ralph Wiggum). They affect presentation only and never change an agent's result.
13.24 Web UI Functions
Server component helpers (renderTemplate, componentFragment, componentLiveEmit, componentState*, onAgentProgress, clearAgentProgress, cancelThink) and ui.* controls live in 26. Web UI Server Components. Start from 25. Web UI Overview if you are choosing a UI model.
13.25 Password, JWT, and request auth
Signature how-to for HTTP lives in 29.16 Password, JWT, and cookie helpers. Request principal: 29.6.2 req.auth. Primers: password hashing, JWT, CSRF.
Catalog names: hashPassword, verifyPassword, createJwt, verifyJwt, generateCsrfToken, verifyCsrfToken, csrfField, createSecureCookie, readSecureCookie, httpBearerToken, httpCookieToken, httpAuthToken, bindForm, formErrors, pageLayout. Handler surface: req.auth.authenticateBearerJwt, authenticateCookieJwt, requireRole, requirePermission, setVerifiedSub.
13.26 Background jobs
Lightweight SQLite queue (enqueueJob, claimJob, completeJob, failJob, getJob, listJobs) — not a durable workflow. How-to: 23.11 Lightweight job queue.
13.27 Optional packs (not in core)
Core BuiltInRegistry lists only open-source core symbols. Domain-specific builtins may ship in separately distributed packs; load them with loadNativeModule(...) and ship any required assemblies beside your app. Core does not auto-register pack globals.
See Also
- Appendix — Optional packs
- Input/Output — console, files, paths, environment, host I/O; prefer
io.* - 12.6 Capability tokens — how-to for
cap.*(this chapter is the catalog) - 10.9.1 AI pipeline helpers —
loadDocuments,runPrompt,composePipe - 12.9 Fetching URLs —
webFetch - Web UI Server Components — Server-side UI and component APIs
- Agent Orchestration — Tools, task plans, and agent logging
- 23.11 Lightweight job queue —
enqueueJoband friends - 24. Agentic Runs —
expect,stream,race/firstOk,trace.*,AgentError - 29.16 Password, JWT, and cookie helpers