AgentScript Composition Examples
AgentScript
function makeTodos() goal: "Create a composable todo service with retry and timeout capabilities" backstory: "Used to fetch todos from an external API with error handling"
// Define the client with composition const client = HttpClient |> filterStatusOk |> prependUrl("https://jsonplaceholder.typicode.com")
// Define list function with retry and concurrency function list(ids: Iterable<number>): Array<unknown> goal: "Fetch multiple todos by their IDs concurrently" requires: ids != null: "IDs must be provided" concurrency: "inherit" return: forEach(ids, (id) => findById(id) |> retry(3)) errors: HttpClientError: "Failed to fetch todos" TimeoutException: "Request timed out"
// Define findById function with timeout function findById(id: number): unknown goal: "Fetch a single todo by ID" requires: id > 0: "ID must be positive" return: client.get(`/todos/${id}`) |> getJson |> scoped |> timeout("1 second") errors: HttpClientError: "Failed to fetch todo" TimeoutException: "Request timed out"
return { list, findById }
tests: success: // Test successful todo fetching list([1, 2]) -> [{ id: 1, ... }, { id: 2, ... }] errors: // Test timeout behavior findById(999) -> TimeoutException performance: // Test concurrent fetching performance list([1..10]) -> expect < 2s
TypeScript (Effect)
const makeTodos = Effect.gen(function*() { const client = (yield* HttpClient.HttpClient).pipe( HttpClient.filterStatusOk, HttpClient.mapRequest(HttpClientRequest.prependUrl("https://jsonplaceholder.typicode.com")) )
// interuption and concurrency can be configured with // composition const list = ( ids: Iterable<number> ): Effect.Effect< Array<unknown>, HttpClientError.HttpClientError | Cause.TimeoutException > => Effect.forEach(ids, (id) => findById(id).pipe(Effect.retry({ times: 3 })), { concurrency: "inherit" })
const findById = ( id: number ): Effect.Effect< unknown, HttpClientError.HttpClientError | Cause.TimeoutException > => client.get(`/todos/${id}`).pipe( Effect.andThen((response) => response.json), Effect.scoped, Effect.timeout("1 second") )
return { list, findById } as const})
class Todos extends Effect.Tag("Todos")<Todos, Effect.Effect.Success<typeof makeTodos>>() { static Live = Layer.effect(Todos, makeTodos).pipe( Layer.provide(FetchHttpClient.layer) )}
const main = Todos.list(Array.range(1, 10)).pipe( Effect.withConcurrency(3), Effect.timeout("10 seconds"), Effect.andThen((todos) => Console.log("Got todos", todos)))
TypeScript (Plain)
// configuration has to be added to the type signatureconst getTodos = ( ids: Iterable<number>, concurrency: number, signal?: AbortSignal,): Promise<any[]> => forEach(ids, concurrency, (id) => withRetries(3, () => getTodo(id, signal)), )
const getTodo = ( id: number, signal?: AbortSignal,): Promise<any> => abortable( (signal) => jsonOk(() => fetch( `https://jsonplaceholder.typicode.com/todos/${id}`, { signal }, ), ), mergeAbortSignal( AbortSignal.timeout(1000), signal, ), )
async function main() { const ids = Array.from( { length: 10 }, (_, i) => i + 1, ) const todos = await getTodos( ids, 3, AbortSignal.timeout(10000) ) console.log("Got todos", todos)}
// ... helper functions ...
The AgentScript version demonstrates several key features:
- Composition using the pipe operator (
|>
) for transforming the HTTP client and request processing - Embedded contracts with
requires
blocks for input validation - Concurrency control through the
concurrency
directive - Error handling with explicit error types
- Timeouts integrated directly into the function definitions
- Built-in testing for success cases, errors, and performance
- Natural language context through
goal
andbackstory
fields
The AgentScript syntax is more concise while maintaining all the functionality of the Effect version, with added benefits of inline testing and contract programming. The transpiler would generate the appropriate TypeScript code with all the necessary Effect.js constructs.