Basic syntax
A lot of Agency syntax is borrowed from TypeScript and Python. If you have used these languages, the code should look similar.
Primitives and variables
You've got primitives: strings, numbers, booleans:
const name: string = "Alice"
const age: number = 30
const isAgent: boolean = trueYou can define variables with let or const.
You can use double quotes, single quotes, or backticks for strings. All three allow string interpolation with ${...}:
const name = "Alice"
const greeting1 = "Hello, ${name}!"
const greeting2 = 'Hello, ${name}!'
const greeting3 = `Hello, ${name}!`To write a literal ${ without starting an interpolation, escape it as \${. This works in every string kind, including triple-quoted strings:
const price = "costs \${5}" // -> costs ${5}
const template = """let x = \${y}""" // -> let x = ${y} (useful for embedding code)Multi-line strings use """ triple-quotes. They also support ${...} interpolation, but otherwise do not interpret backslash escapes — \n is a backslash followed by an n, not a newline. The one exception is \${, which escapes an interpolation as shown above:
const block = """
${name} on the first line\n
literal \${skip} on the second
"""Arrays and objects
You can define arrays and objects:
const names = ["Alice", "Bob", "Charlie"]
const person = { name: "Alice", age: 30 }If statements
if (age > 18) {
print("You are an adult.")
} else if (age == 18) {
print("You are exactly 18 years old.")
} else {
print("You are a minor.")
}Agency has no ternary (? :). Instead, if ... then ... else can be used as a value when assigning to a variable or in a return:
const label = if isProd then "Production" else "Local"
def describe(n: number): string {
return if n > 100 then "big" else "small"
}The else is required (the expression always produces a value). Like match expressions, an if expression is only allowed as a const/let value or a return — not nested inside another value such as an object field or an argument. To keep them readable they are also deliberately flat: no else if, and a branch cannot itself be an if ... then ... else. For more than two cases, or nested conditions, use match.
Type annotations
You can add type annotations, just like TypeScript.
const name: string = "Alice"
const age: number = 30
const names: string[] = ["Alice", "Bob", "Charlie"]Types are covered in more detail in the section on types.
Loops
While loop:
while (age < 100) {
print(`You are ${age} years old.`)
age = age + 1
}For loop:
const names = ["Alice", "Bob", "Charlie"]
for (name in names) {
print(name)
}
// or with index:
for (name, i in names) {
print(`Person ${i}: ${name}`)
}For loop with objects:
const person = { name: "Alice", age: 30 }
for (key, value in person) {
print(`${key}: ${value}`)
}
// or just the key:
for (key in person) {
print(`${key}: ${person[key]}`)
}The second loop variable depends on what you're iterating: for an array it's the numeric index, and for an object it's the value at that key.
For loops can also destructure arrays and objects:
const people = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
]
for ({ name, age } in people) {
print(`${name} is ${age} years old.`)
}Other loop constructs, such as map, are part of the agency standard library.
Comments
You can have single-line or multi-line comments.
// This is a single-line comment
/*
This is a multi-line comment
*/Or doc comments for documentation generation:
/** This is a doc comment for the Person type */
type Person = {
name: string
age: number
}Doc comments are wrapped in /** ... */ and must be on their own line. They can be used to document types, functions, and variables. Doc comments support Markdown formatting.
You can also write a module-level doc comment using the @module tag. This documents the file itself and appears at the top of the generated documentation:
/** @module
This module provides utilities for working with dates.
*/Note: comments must be on their own line, they cannot be at the end of a line containing code.
Not allowed:
const x = 5 // this is a commentFunctions
You can define functions:
def greet(name: string): string {
return `Hello, ${name}!`
}Named arguments
You can call functions with named arguments:
def greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`
}
greet(name: "Adit")Functions are covered in more detail in the section on functions.
Nodes
Nodes are like functions, but they are the entry points into your agent:
node greet(name: string): string {
return `Hello, ${name}!`
}Nodes are covered in more detail in the section on nodes.
Blocks
Although Agency doesn't have lambdas the way JavaScript does, it has a similar feature called blocks. You can use this to define functions that take another function. For example, let's use Agency's built-in map function, which takes a block:
const numbers = [1, 2, 3, 4, 5]
const squares = map(numbers) as n {
return n * n
}There are also inline blocks:
const numbers = [1, 2, 3, 4, 5]
const squares = map(numbers, \n -> n * n)Blocks are covered in more detail in the section on blocks.
Regexes
Regexes are also supported as a primitive:
// you must use the `re` prefix:
const regex = re/(foo|bar)/
// Use the =~ operator to test if a string matches a regex:
if (name =~ re/^A/) {
print("Your name starts with A!")
}
// or !~ to test if it doesn't match:
if (str !~ regex) {
print("The string does not match the regex.")
}Array slice syntax
Python-style array slice syntax is supported:
let arr = [1, 2, 3, 4, 5]
// sliced is [2, 3, 4]
const sliced = arr[1:4]
// slicedToEnd is [3, 4, 5]
const slicedToEnd = arr[2:]
// slicedFromStart is [1, 2, 3]
const slicedFromStart = arr[:3]
// negativeSlice is [3, 4]
const negativeSlice = arr[-3:-1]
// arr is now [10, 20, 30, 4, 5]
arr[:3] = [10, 20, 30]Unit literals
Agency supports unit literals for time, cost, and size values. They compile to plain numbers at compile time:
// time
const timeout = 30s // compiles to 30000 (milliseconds)
const delay = 500ms // compiles to 500
const duration = 2h // compiles to 7200000
const week = 1w // compiles to 604800000
// cost
const budget = $5.00 // compiles to 5.00
// size
const size = 100KB // compiles to bytes
const mediumSize = 500MB // compiles to bytes
const bigSize = 2GB // compiles to bytesSupported time units: ms (milliseconds), s (seconds), m (minutes), h (hours), d (days), w (weeks). All time units normalize to milliseconds.
Supported cost units: $ (dollars).
Supported size units: kb (kilobytes), mb (megabytes), gb (gigabytes). Case insensitive. All size units normalize to bytes.
Unit math works:
1s + 500ms // 1000 + 500 = 1500
2s * 3 // 2000 * 3 = 6000
if (elapsed > 30s) { ... }Destructuring and pattern matching
Array and object destructuring work in let / const declarations and in for loops:
let [a, b, ...rest] = items
let { name, age } = person
for ({ name, age } in users) { ... }Pattern matching is covered in the section on pattern matching.
Misc
Reserved names
Variables and functions beginning with two underscores (__name) are reserved for the compiler and runtime, so you cannot use them in your code.
JavaScript features that don't exist in Agency
- Lambdas.
- Async/await. Everything is awaited by default, and there are specific constructs for concurrency.
- Classes.