- Proposal: ARO-0016
- Author: ARO Language Team
- Status: Implemented
- Requires: ARO-0001, ARO-0006
This proposal defines how ARO interoperates with external libraries and services. ARO uses a Swift Package-based Service architecture where external functionality is wrapped in services and invoked via the <Call> action.
Real-world applications require integration with:
- HTTP APIs: REST, GraphQL endpoints
- Databases: PostgreSQL, MongoDB, Redis
- Media Processing: FFmpeg, ImageMagick
- System Libraries: Encryption, compression
ARO provides a simple, unified approach: external libraries become Services.
One Action, Many Services
All external integrations use the same pattern:
Call the <result> from the <service: method> with { args }.
Call the <result> from the <service: method> with { key: value, ... }.
| Component | Description |
|---|---|
result |
Variable to store the result |
service |
Service name (e.g., http, postgres, ffmpeg) |
method |
Method to invoke (e.g., get, query, transcode) |
args |
Key-value arguments |
(* HTTP GET request *)
Call the <response> from the <http: get> with {
url: "https://api.example.com/users"
}.
(* Database query *)
Call the <users> from the <postgres: query> with {
sql: "SELECT * FROM users WHERE active = true"
}.
(* Media transcoding *)
Call the <result> from the <ffmpeg: transcode> with {
input: "/path/to/video.mov",
output: "/path/to/video.mp4",
format: "mp4"
}.
The http service is built-in and provides HTTP request capabilities.
(* GET request *)
Call the <response> from the <http: get> with {
url: "https://api.example.com/data",
headers: { "Authorization": "Bearer token123" }
}.
(* POST request *)
Call the <response> from the <http: post> with {
url: "https://api.example.com/users",
body: { name: "Alice", email: "alice@example.com" },
headers: { "Content-Type": "application/json" }
}.
(* Other methods: put, patch, delete *)
Call the <response> from the <http: delete> with {
url: "https://api.example.com/users/123"
}.
Response format:
{
"status": 200,
"headers": { "Content-Type": "application/json" },
"body": { ... }
}Services are Swift types that implement the AROService protocol.
public protocol AROService: Sendable {
/// Service name (e.g., "postgres", "redis")
static var name: String { get }
/// Initialize the service
init() throws
/// Call a method
func call(_ method: String, args: [String: any Sendable]) async throws -> any Sendable
/// Shutdown (optional)
func shutdown() async
}import PostgresNIO
public struct PostgresService: AROService {
public static let name = "postgres"
private let pool: PostgresConnectionPool
public init() throws {
let config = PostgresConnection.Configuration(...)
pool = try PostgresConnectionPool(configuration: config)
}
public func call(_ method: String, args: [String: any Sendable]) async throws -> any Sendable {
switch method {
case "query":
let sql = args["sql"] as! String
let rows = try await pool.query(sql)
return rows.map { row in
// Convert to dictionary
}
case "execute":
let sql = args["sql"] as! String
try await pool.execute(sql)
return ["success": true]
default:
throw ServiceError.unknownMethod(method, service: Self.name)
}
}
public func shutdown() async {
await pool.close()
}
}Services are registered with the ServiceRegistry:
try ServiceRegistry.shared.register(PostgresService())When ARO is distributed as a pre-compiled binary, users can add custom services via plugins.
Plugins can be either single Swift files or Swift packages with dependencies:
Simple Plugin (single file):
MyApp/
├── main.aro
├── openapi.yaml
└── plugins/
└── MyService.swift
Package Plugin (with dependencies):
MyApp/
├── main.aro
├── openapi.yaml
└── plugins/
└── MyPlugin/
├── Package.swift
└── Sources/MyPlugin/
└── MyService.swift
Plugins use a C-compatible JSON interface for maximum portability:
// plugins/GreetingService.swift
import Foundation
/// Plugin initialization - returns service metadata as JSON
/// Tells ARO what services and symbols this plugin provides
@_cdecl("aro_plugin_init")
public func pluginInit() -> UnsafePointer<CChar> {
let metadata = """
{"services": [{"name": "greeting", "symbol": "greeting_call"}]}
"""
return UnsafePointer(strdup(metadata)!)
}
/// Service entry point - C-callable interface
/// - Parameters:
/// - methodPtr: Method name (C string)
/// - argsPtr: Arguments as JSON (C string)
/// - resultPtr: Output - result as JSON (caller must free)
/// - Returns: 0 for success, non-zero for error
@_cdecl("greeting_call")
public func greetingCall(
_ methodPtr: UnsafePointer<CChar>,
_ argsPtr: UnsafePointer<CChar>,
_ resultPtr: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
) -> Int32 {
let method = String(cString: methodPtr)
let argsJSON = String(cString: argsPtr)
// Parse arguments
var args: [String: Any] = [:]
if let data = argsJSON.data(using: .utf8),
let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
args = parsed
}
// Execute method
let name = args["name"] as? String ?? "World"
let result: String
switch method.lowercased() {
case "hello":
result = "Hello, \(name)!"
case "goodbye":
result = "Goodbye, \(name)!"
default:
let errorJSON = "{\"error\": \"Unknown method: \(method)\"}"
resultPtr.pointee = strdup(errorJSON)
return 1
}
// Return result as JSON
let resultJSON = "{\"result\": \"\(result)\"}"
resultPtr.pointee = strdup(resultJSON)
return 0
}For plugins that need external libraries, use a Swift package:
// plugins/ZipPlugin/Package.swift
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "ZipPlugin",
platforms: [.macOS(.v13)],
products: [
.library(name: "ZipPlugin", type: .dynamic, targets: ["ZipPlugin"])
],
dependencies: [
.package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.0")
],
targets: [
.target(name: "ZipPlugin", dependencies: ["Zip"])
]
)- ARO scans
./plugins/directory - For
.swiftfiles: compiles to.dylibusingswiftc - For directories with
Package.swift: builds usingswift build - Loads dynamic library via
dlopen - Calls
aro_plugin_initto get service metadata (JSON) - Registers each service with the symbol from metadata
Compiled plugins are cached in .aro-cache/ and only recompiled when source changes.
The aro_plugin_init function returns JSON describing available services:
{
"services": [
{"name": "greeting", "symbol": "greeting_call"},
{"name": "translator", "symbol": "translator_call"}
]
}Each service entry specifies:
name: Service name used in ARO code (<greeting: hello>)symbol: C function symbol to call (greeting_call)
openapi: 3.0.3
info:
title: Weather Service
version: 1.0.0
paths: {}
components:
schemas:
WeatherData:
type: object
properties:
temperature:
type: number
conditions:
type: string
location:
type: string(Application-Start: Weather Service) {
Log "Weather Service starting..." to the <console>.
(* Fetch weather from external API *)
Call the <response> from the <http: get> with {
url: "https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true"
}.
Extract the <weather> from the <response: body>.
Log "Current weather:" to the <console>.
Log <weather> to the <console>.
Return an <OK: status> for the <startup>.
}
(Application-End: Success) {
Log "Weather Service shutting down..." to the <console>.
Return an <OK: status> for the <shutdown>.
}
| Method | Arguments | Description |
|---|---|---|
get |
url, headers? |
HTTP GET request |
post |
url, body, headers? |
HTTP POST request |
put |
url, body, headers? |
HTTP PUT request |
patch |
url, body, headers? |
HTTP PATCH request |
delete |
url, headers? |
HTTP DELETE request |
- Application loads, discovers
aro.yaml - Swift Package Manager loads service packages
- Services register with
ServiceRegistry <Call>action looks up service and invokes method
aro buildreadsaro.yaml, includes service packages in link- LLVM IR calls
aro_action_call→ Swift runtime - Swift runtime looks up service in
ServiceRegistry - Service method executes
ARO's interoperability is built on a simple principle:
| Concept | Implementation |
|---|---|
| External libraries | Swift Package Services |
| Invocation | <Call> action |
| Custom services | Plugin system |
| Configuration | aro.yaml |
This approach provides:
- Simplicity: One action for all external calls
- Extensibility: Easy to add new services
- Portability: Works in interpreter and compiler modes
- Swift Integration: Leverages Swift ecosystem
| Version | Date | Changes |
|---|---|---|
| 2.0 | 2024-12 | Simplified to Service-based architecture |
| 1.0 | 2024-01 | Initial specification with complex syntax |