-
Notifications
You must be signed in to change notification settings - Fork 0
Language Tour
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.
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 "Hello, World!" to the <console>.
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.
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
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 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.
ARO actions are categorized by their semantic role:
Bring data from external sources into your feature set:
Extract the <data> from the <request: body>.
Retrieve the <user> from the <user-repository>.
Request the <weather> from the <weather-api>.
Parse the <config> from the <json-string>.
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>.
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>.
Publish data or send to external systems:
Store the <user> into the <user-repository>.
Publish as <current-user> <user>.
Log <message> to the <console>.
Send the <email> to the <user: email>.
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> |
Every ARO application has a defined lifecycle:
The entry point - exactly one per application:
(Application-Start: My Application) {
Log "Starting..." to the <console>.
(* Keep the application running to process events *)
Keepalive the <application> for the <events>.
Return an <OK: status> for the <startup>.
}
Optional exit handlers for cleanup:
(* Called on graceful shutdown *)
(Application-End: Success) {
Log "Shutting down..." to the <console>.
Return an <OK: status> for the <shutdown>.
}
(* Called on error/crash *)
(Application-End: Error) {
Extract the <error> from the <shutdown: error>.
Log <error> to the <console>.
Return an <OK: status> for the <error-handling>.
}
Feature sets don't call each other directly. Instead, they respond to events:
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: getUserhandlers.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>.
}
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>.
}
Handle TCP connections:
(Handle Client Connected: ClientConnected Handler) {
Extract the <client-id> from the <event: connectionId>.
Log <client-id> to the <console>.
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>.
}
ARO uses when clauses for conditional execution and match/case for multi-way branching.
A statement executes only if its when condition is true:
(* Only send notification when user has email *)
Send the <notification> to the <user: email> when <user: email> exists.
(* Log admin access *)
Log <admin-access> to the <audit> when <user: role> = "admin".
(* Return error when validation fails *)
Return a <BadRequest: status> for the <request> when <user-id> is empty.
For multi-way branching, use match/case/otherwise:
match <user: status> {
case "active" {
Return an <OK: status> with <user>.
}
case "pending" {
Send the <verification-email> to the <user: email>.
Return a <PendingVerification: status> for the <request>.
}
case "locked" {
Return an <AccountLocked: error> for the <request>.
}
otherwise {
Return an <InvalidStatus: error> for the <request>.
}
}
Note: ARO follows the "happy path" philosophy—you write only the success case, and the runtime handles errors automatically.
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>.
}
ARO uses contract-first API development where your API is defined in openapi.yaml before any code:
MyAPI/
├── openapi.yaml # Required: Defines all HTTP routes
├── main.aro # Application entry point
└── handlers.aro # Feature sets matching operationIds
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(Application-Start: Product API) {
Log "Product API starting..." to the <console>.
Keepalive the <application> for the <events>.
Return an <OK: status> for the <startup>.
}
(Application-End: Success) {
Log "Shutting down..." to the <console>.
Return an <OK: status> for the <shutdown>.
}
(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>.
}
Make outgoing HTTP requests:
(Fetch Weather: External API) {
Extract the <city> from the <queryParameters: city>.
Request the <weather-data> from "https://api.weather.com/v1/weather?city=${city}".
Return an <OK: status> with <weather-data>.
}
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>.
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>.
}
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 *)
Here's a complete multi-file application with contract-first HTTP:
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(Application-Start: Task Manager) {
Log "Starting Task Manager..." to the <console>.
Log "Task Manager running on port 8080" to the <console>.
Keepalive the <application> for the <events>.
Return an <OK: status> for the <startup>.
}
(Application-End: Success) {
Log "Shutting down..." to the <console>.
Return an <OK: status> for the <shutdown>.
}
(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>.
}
(Log New Task: TaskCreated Handler) {
Extract the <task> from the <event: task>.
Extract the <title> from the <task: title>.
Log "New task: ${title}" to the <console>.
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 "Completed: ${title}" to the <console>.
Return an <OK: status> for the <notification>.
}
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
Fundamentals
- The Basics
- Feature Sets
- Actions
- Variables
- Type System
- Control Flow
- Error Handling
- Computations
- Dates
- Concurrency
Runtime & Events
I/O & Communication
Advanced