Functions & Side-Effects ​
In Carotene, a function is a discrete, triggered workflow. It represents an action that runs in response to a user request or an event, and it always returns a finite result (a Future).
Crucially, you do not write the implementation code inside your .carrot file. Instead, you use the function block to define a Zero-Trust Permission Matrix. You define the inputs, the outputs, and the exact side-effects the AI agent is allowed to execute.
The Execution Sandbox ​
When the Carotene CLI hands your function contract to the AI code generator, it builds a strictly sandboxed environment based entirely on the verbs you provide.
If you do not grant a permission at the architectural level, the AI physically cannot perform the action in the codebase.
domain Commerce {
backend {
// 1. The Signature: Defines the exact network payload (DTO vs Entity Binding)
function ProcessCheckout(payload: CheckoutPayload) -> Future<store.Order> {
// 2. The Permission Matrix (Verbs)
reads store.Product // Allowed to check stock
creates store.Order // Allowed to insert the new order
mutates state.Metrics // Allowed to update the volatile memory cache
// 3. Execution Logic
implements {
@("Calculate total, process payment, and create order")
}
}
}
}How the Sandbox Works: Capabilities-Based Security ​
Carotene does not rely on "system prompts" to keep the AI in check, nor does it rely on dynamic runtime checks that can be bypassed. It enforces Zero-Trust using Capabilities-Based Dependency Injection.
When the compiler generates your src/ implementation stub, it does not give the function global access to your database ORM or a raw fetch client. Instead, it generates a highly specific Context object tailored only to the verbs you declared.
// .generated/api/ProcessCheckout.ts
// The compiler generates an interface omitting unauthorised methods
export interface ProcessCheckoutContext {
db: {
Order: { create: (data: any) => Promise<void> },
Product: { read: (id: string) => Promise<any> }
// Notice 'delete' and 'update' are physically missing
},
integrations: {
Stripe: { ChargeCustomer: (amount: number) => Promise<boolean> }
}
}1. The Database Shield ​
When the AI receives this ctx object, its environment only contains ctx.db.Product.read() and ctx.db.Order.create(). If the AI hallucinates and writes await ctx.db.User.delete(), the native TypeScript/Go compiler instantly fails the build because the method literally does not exist on the injected context.
2. The Network Proxy ​
Raw network access (fetch, axios, http) is banned. All external integrations are generated as local boilerplate proxies (ctx.integrations.Stripe...). To the AI, charging a credit card feels exactly like calling a local, synchronous function. Because all traffic must flow through these generated proxies, Carotene can natively mock them during tests and mathematically guarantee no rogue data escapes your network boundary.
Integrations (Managing External Side-Effects) ​
Database access is only half the battle. Modern applications rely heavily on third-party APIs (Stripe, SendGrid, Twilio).
In standard Node.js/Python, an AI can simply write fetch('https://any-url.com'). This is a massive security vulnerability. In Carotene, raw HTTP requests are banned in the execution sandbox. To talk to the outside world, you must define an integration and grant the AI permission to use it via the calls verb.
1. Defining the Integration ​
You define external services directly inside the backend or frontend that requires them. This guarantees that your execution environments remain completely decoupled and secure.
backend OrderAPI {
// Scoped integrations
integration Stripe {
function ChargeCustomer(amount: Float, source: String) -> Future<Boolean>
}
integration SendGrid {
function SendEmail(to: String, templateId: String)
}
function CompleteOrder(orderId: store.Order.id) {
reads store.Order
// Explicitly grants the AI permission to trigger these scoped side-effects
calls integration.Stripe.ChargeCustomer
calls integration.SendGrid.SendEmail
triggers store.Order.status.ProcessPayment
updates store.Order
implements {
@("Charge the customer, update status, and send confirmation email")
}
}
}2. Granting the Side-Effect ​
Inside your business logic, you explicitly grant the AI permission to trigger these external services using the calls verb.
The Security Guarantee: The AI can call ctx.Stripe.ChargeCustomer(). But if it tries to write a raw fetch() or use axiosto send data to an unauthorized URL, the sandbox rejects the code. You have total architectural control over data egress.
Full-Stack RPC (Crossing the Network Boundary) ​
Functions do not just live on the backend. Carotene is a full-stack language, meaning your frontend UI components need to trigger these backend workflows.
You do this using the exact same calls verb, completely eliminating the need to write REST API clients, fetch wrappers, or GraphQL queries.
frontend {
// A UI component definition
view CheckoutButton(currentOrder: backend.store.Order) {
// The frontend explicitly calls the backend function, passing the exact required type
calls backend.CompleteOrder
implements {
<button onClick={() => backend.CompleteOrder(currentOrder.id)}>
Pay Now
</button>
}
}
}Auto-Generated UI States (isLoading) ​
When the Carotene compiler sees a frontend block calling a backend function, it automatically generates the end-to-end RPC plumbing (like tRPC or gRPC).
But it goes a step further for UI developers. For discrete functions (non-streams), the generated frontend SDK automatically exposes reactive listeners. Just as the state machine generates .canExecute(), the RPC layer generates an .isLoading property.
// Inside your generated React/Vue UI component
<button
disabled={!backend.CompleteOrder.canExecute(currentOrder.status) || backend.CompleteOrder.isLoading}
onClick={() => backend.CompleteOrder(currentOrder.id)}
>
{backend.CompleteOrder.isLoading ? "Processing..." : "Pay Now"}
</button>By generating these properties natively in the SDK, Carotene eliminates the need for developers (and AI UI generators) to write tedious useState(false) boilerplate to manage network lifecycles.