Skip to content

Language Tour

Kris Simon edited this page Mar 1, 2026 · 7 revisions

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 "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.

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:

  1. Performs an action (verb like Extract, Retrieve, Create)
  2. Produces a result (variable to store the outcome)
  3. 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>.
Request 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 <message> to 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 "Starting..." to the <console>.

    (* 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 "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>.
}

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 <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>.
}

Conditional Execution

ARO uses when clauses for conditional execution and match/case for multi-way branching.

When Clauses

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.

Match Expressions

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.

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 "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>.
}

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>.
    Request 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 "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>.
}

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 "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>.
}

Next Steps

This tour has introduced you to ARO's key features. To learn more:

Clone this wiki locally