A Tour of ARO
This tour gives you a comprehensive overview of ARO by exploring its features through examples. By the end, you'll understand how ARO programs are structured and how to build event-driven applications.
Hello, World!
Tradition suggests that the first program in a new language should print "Hello, World!" Here's how you do it in ARO:
(Application-Start: Hello World) {
<Log> the <message> for the <console> with "Hello, World!".
<Return> an <OK: status> for the <startup>.
}
If you've programmed before, this syntax might look unusual. ARO is designed to read like natural language while remaining precise and unambiguous.
Feature Sets
The fundamental unit of organization in ARO is the feature set. A feature set groups related statements that accomplish a business goal:
(Calculate Order Total: Order Processing) {
<Extract> the <items> from the <order: lineItems>.
<Compute> the <subtotal> for the <items>.
<Compute> the <tax> for the <subtotal>.
<Compute> the <total> from the <subtotal> with <tax>.
<Return> an <OK: status> with <total>.
}
Every feature set has:
- A name (
Calculate Order Total) - A business activity (
Order Processing) - A body containing statements
The ARO Statement
ARO statements follow the Action-Result-Object pattern:
<Action> the <result> preposition the <object>.
For example:
<Extract> the <user-id> from the <pathParameters: id>.
<Retrieve> the <user> from the <user-repository>.
<Create> the <response> with <user>.
<Return> an <OK: status> with <response>.
Each statement:
- Performs an action (verb like Extract, Retrieve, Create)
- Produces a result (variable to store the outcome)
- Uses an object (source of data or target of action)
Variables and Qualifiers
Variables in ARO use angle brackets and can have qualifiers:
<user> (* Simple variable *)
<user: id> (* Variable with qualifier *)
<request: body> (* Access nested property *)
<user-repository> (* Hyphenated names allowed *)
Variables are automatically typed based on context. Qualifiers provide additional context about what aspect of a variable you're accessing.
Actions by Role
ARO actions are categorized by their semantic role:
REQUEST Actions
Bring data from external sources into your feature set:
<Extract> the <data> from the <request: body>.
<Retrieve> the <user> from the <user-repository>.
<Fetch> the <weather> from the <weather-api>.
<Parse> the <config> from the <json-string>.
OWN Actions
Create or transform data within your feature set:
<Create> the <user> with <user-data>.
<Compute> the <total> for the <items>.
<Transform> the <dto> from the <entity>.
<Validate> the <input> for the <schema>.
RESPONSE Actions
Send results back from your feature set:
<Return> an <OK: status> with <data>.
<Return> a <NotFound: status> for the <missing: user>.
<Throw> an <ValidationError> for the <invalid: input>.
EXPORT Actions
Publish data or send to external systems:
<Store> the <user> into the <user-repository>.
<Publish> as <current-user> <user>.
<Log> the <message> for the <console>.
<Send> the <email> to the <user: email>.
Prepositions
Different prepositions convey different relationships:
| Preposition | Meaning | Example |
|---|---|---|
from |
Source of data | <Extract> the <id> from the <request> |
for |
Purpose/target | <Compute> the <hash> for the <password> |
with |
Additional data | <Create> the <user> with <data> |
into |
Storage destination | <Store> the <user> into the <repository> |
to |
Recipient | <Send> the <message> to the <user> |
against |
Comparison target | <Compare> the <hash> against the <stored-hash> |
Application Lifecycle
Every ARO application has a defined lifecycle:
Application-Start
The entry point - exactly one per application:
(Application-Start: My Application) {
<Log> the <startup: message> for the <console> with "Starting...".
(* Keep the application running to process events *)
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
Application-End
Optional exit handlers for cleanup:
(* Called on graceful shutdown *)
(Application-End: Success) {
<Log> the <shutdown: message> for the <console> with "Shutting down...".
<Return> an <OK: status> for the <shutdown>.
}
(* Called on error/crash *)
(Application-End: Error) {
<Extract> the <error> from the <shutdown: error>.
<Log> the <error: message> for the <console> with <error>.
<Return> an <OK: status> for the <error-handling>.
}
Event-Driven Programming
Feature sets don't call each other directly. Instead, they respond to events:
HTTP Events (Contract-First)
ARO uses contract-first HTTP development. Routes are defined in an openapi.yaml file, and feature sets are named after the operationId values.
openapi.yaml:
openapi: 3.0.3
info:
title: User API
version: 1.0.0
paths:
/users:
get:
operationId: listUsers # Feature set name
post:
operationId: createUser
/users/{id}:
get:
operationId: getUser
handlers.aro:
(* Feature sets are named after operationId values *)
(listUsers: User API) {
<Retrieve> the <users> from the <user-repository>.
<Return> an <OK: status> with <users>.
}
(getUser: User API) {
<Extract> the <user-id> from the <pathParameters: id>.
<Retrieve> the <user> from the <user-repository> where id = <user-id>.
<Return> an <OK: status> with <user>.
}
(createUser: User API) {
<Extract> the <user-data> from the <request: body>.
<Create> the <user> with <user-data>.
<Store> the <user> into the <user-repository>.
<Return> a <Created: status> with <user>.
}
File System Events
React to file system changes:
(Process Upload: FileCreated Handler) {
<Extract> the <path> from the <event: path>.
<Read> the <content> from the <file: path>.
<Transform> the <processed> from the <content>.
<Store> the <processed> into the <processed-repository>.
<Return> an <OK: status> for the <processing>.
}
Socket Events
Handle TCP connections:
(Handle Client Connected: ClientConnected Handler) {
<Extract> the <client-id> from the <event: connectionId>.
<Log> the <connection: message> for the <console> with <client-id>.
<Return> an <OK: status> for the <connection>.
}
(Echo Data: DataReceived Handler) {
<Extract> the <data> from the <event: data>.
<Extract> the <client> from the <event: connection>.
<Send> the <data> to the <client>.
<Return> an <OK: status> for the <echo>.
}
Control Flow
ARO supports conditional logic:
If-Then-Else
(getUser: User API) {
<Extract> the <user-id> from the <pathParameters: id>.
<Retrieve> the <user> from the <user-repository> where id = <user-id>.
if <user> is empty then {
<Return> a <NotFound: status> for the <missing: user>.
}
<Return> an <OK: status> with <user>.
}
Guards
(updateUser: User API) {
<Extract> the <user-id> from the <pathParameters: id>.
<Extract> the <updates> from the <request: body>.
when <user-id> is empty {
<Return> a <BadRequest: status> for the <missing: id>.
}
when <updates> is empty {
<Return> a <BadRequest: status> for the <missing: data>.
}
<Retrieve> the <user> from the <user-repository> where id = <user-id>.
<Transform> the <updated-user> from the <user> with <updates>.
<Store> the <updated-user> into the <user-repository>.
<Return> an <OK: status> with <updated-user>.
}
Multi-File Applications
ARO applications can span multiple files without imports:
MyApp/
├── openapi.yaml # API contract (required for HTTP)
├── main.aro # Application-Start, Application-End
├── users.aro # User feature sets
├── orders.aro # Order feature sets
└── notifications.aro # Notification handlers
All feature sets are automatically visible to each other. Published variables are shared:
users.aro:
(Load Default User: Initialization) {
<Retrieve> the <admin> from the <user-repository> where role = "admin".
<Publish> as <system-admin> <admin>.
<Return> an <OK: status> for the <loading>.
}
notifications.aro:
(Send Admin Alert: CriticalError Handler) {
(* Access published variable from users.aro *)
<Extract> the <admin-email> from the <system-admin: email>.
<Send> the <alert> to the <admin-email>.
<Return> an <OK: status> for the <alert>.
}
Contract-First HTTP Server
ARO uses contract-first API development where your API is defined in openapi.yaml before any code:
Application Structure
MyAPI/
├── openapi.yaml # Required: Defines all HTTP routes
├── main.aro # Application entry point
└── handlers.aro # Feature sets matching operationIds
openapi.yaml
openapi: 3.0.3
info:
title: Product API
version: 1.0.0
paths:
/products:
get:
operationId: listProducts
post:
operationId: createProduct
/products/{id}:
get:
operationId: getProduct
put:
operationId: updateProduct
delete:
operationId: deleteProduct
main.aro
(Application-Start: Product API) {
<Log> the <startup: message> for the <console> with "Product API starting...".
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Log> the <shutdown: message> for the <console> with "Shutting down...".
<Return> an <OK: status> for the <shutdown>.
}
handlers.aro
(listProducts: Product API) {
<Retrieve> the <products> from the <product-repository>.
<Return> an <OK: status> with <products>.
}
(createProduct: Product API) {
<Extract> the <product-data> from the <request: body>.
<Validate> the <product-data> for the <product-schema>.
<Create> the <product> with <product-data>.
<Store> the <product> into the <product-repository>.
<Return> a <Created: status> with <product>.
}
(getProduct: Product API) {
<Extract> the <product-id> from the <pathParameters: id>.
<Retrieve> the <product> from the <product-repository> where id = <product-id>.
<Return> an <OK: status> with <product>.
}
(updateProduct: Product API) {
<Extract> the <product-id> from the <pathParameters: id>.
<Extract> the <updates> from the <request: body>.
<Retrieve> the <product> from the <product-repository> where id = <product-id>.
<Transform> the <updated> from the <product> with <updates>.
<Store> the <updated> into the <product-repository>.
<Return> an <OK: status> with <updated>.
}
(deleteProduct: Product API) {
<Extract> the <product-id> from the <pathParameters: id>.
<Delete> the <product> from the <product-repository> where id = <product-id>.
<Return> a <NoContent: status> for the <deletion>.
}
HTTP Client
Make outgoing HTTP requests:
(Fetch Weather: External API) {
<Extract> the <city> from the <queryParameters: city>.
<Fetch> the <weather-data> from "https://api.weather.com/v1/weather?city=${city}".
<Return> an <OK: status> with <weather-data>.
}
File Operations
Read and write files:
(* Reading files *)
<Read> the <content> from the <file: "./config.json">.
<Read> the <config: JSON> from the <file: "./settings.json">.
(* Writing files *)
<Write> the <data> to the <file: "./output.txt">.
<Store> the <log-entry> into the <file: "./logs/app.log">.
(* Watching directories *)
<Watch> the <directory: "./uploads"> as <file-monitor>.
Comments
ARO uses block comments:
(* This is a single-line comment *)
(*
This is a
multi-line comment
*)
(Calculate Total: Order Processing) {
(* Extract line items from the order *)
<Extract> the <items> from the <order: lineItems>.
(* Calculate the sum *)
<Compute> the <total> for the <items>.
<Return> an <OK: status> with <total>.
}
Response Status Codes
ARO maps return statuses to HTTP status codes:
<Return> an <OK: status> with <data>. (* 200 OK *)
<Return> a <Created: status> with <resource>. (* 201 Created *)
<Return> a <NoContent: status> for <deletion>. (* 204 No Content *)
<Return> a <BadRequest: status> for <error>. (* 400 Bad Request *)
<Return> a <NotFound: status> for <missing>. (* 404 Not Found *)
<Return> a <Forbidden: status> for <denied>. (* 403 Forbidden *)
Complete Example
Here's a complete multi-file application with contract-first HTTP:
openapi.yaml
openapi: 3.0.3
info:
title: Task Manager API
version: 1.0.0
paths:
/tasks:
get:
operationId: listTasks
post:
operationId: createTask
/tasks/{id}/complete:
put:
operationId: completeTask
main.aro
(Application-Start: Task Manager) {
<Log> the <startup: message> for the <console> with "Starting Task Manager...".
<Log> the <ready: message> for the <console> with "Task Manager running on port 8080".
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Log> the <shutdown: message> for the <console> with "Shutting down...".
<Return> an <OK: status> for the <shutdown>.
}
tasks.aro
(listTasks: Task API) {
<Retrieve> the <tasks> from the <task-repository>.
<Return> an <OK: status> with <tasks>.
}
(createTask: Task API) {
<Extract> the <task-data> from the <request: body>.
<Create> the <task> with <task-data>.
<Store> the <task> into the <task-repository>.
<Return> a <Created: status> with <task>.
}
(completeTask: Task API) {
<Extract> the <task-id> from the <pathParameters: id>.
<Retrieve> the <task> from the <task-repository> where id = <task-id>.
<Transform> the <completed-task> from the <task> with { completed: true }.
<Store> the <completed-task> into the <task-repository>.
<Return> an <OK: status> with <completed-task>.
}
notifications.aro
(Log New Task: TaskCreated Handler) {
<Extract> the <task> from the <event: task>.
<Extract> the <title> from the <task: title>.
<Log> the <notification: message> for the <console> with "New task: ${title}".
<Return> an <OK: status> for the <notification>.
}
(Log Completed Task: TaskCompleted Handler) {
<Extract> the <task> from the <event: task>.
<Extract> the <title> from the <task: title>.
<Log> the <notification: message> for the <console> with "Completed: ${title}".
<Return> an <OK: status> for the <notification>.
}
Next Steps
This tour has introduced you to ARO's key features. To learn more:
- The Basics - Detailed syntax reference
- Feature Sets - Feature set patterns
- Events - Event-driven architecture
- HTTP Services - Building APIs
- Action Developer Guide - Custom actions