Declaring effects
Every side effect in Cleat is declared with a needs clause. A function without needs is pure — it cannot perform I/O, make HTTP calls, or access the LLM.
Syntax
cleat
fn fetch(url: string) -> Result[string, string] needs { net } { return http.get(url) } fn pure_transform(s: string) -> string { return strings.to_upper(s) // no effects needed }
Built-in effects
| Effect | Grants access to |
|---|---|
llm | LLM API calls (agent.run, llm.prompt, llm.generate, llm.tokens) |
net | Network access (http.get, http.post, http.send, server.start) |
fs | File system (fs.read, fs.write, fs.list, memory.*) |
io | Process I/O (io.print, env.get, os.exit, supervisor.trace) |
log | Logging (log.debug, log.info, log.warn, log.error) |
crypto | Cryptographic operations |
time | Wall-clock access (time.now) |
db | Database access (future) |
git | Git operations (future) |
Compile-time enforcement
cleat
fn bad() -> string { return http.get("url") // ← compile error: missing needs effect: net }
The compiler produces:
text
error: missing needs effect: net
--> main.cleat:2:12
|
2 | return http.get("url")
| ^Transitive enforcement
If function A calls function B which needs { net }, then A must also declare needs { net }:
cleat
fn fetch(url: string) -> Result[string, string] needs { net } { return http.get(url) } fn process() -> Result[string, string] needs { net } { // must declare net return fetch("https://api.example.com") }
Multiple effects
Declare multiple effects with commas:
cleat
fn main() -> int needs { llm, net, io, log } { log.info("starting") let result = agent.run("prompt")? io.println(result) return 0 }
Effects on primitives
| Primitive | Effect rules |
|---|---|
agent | Must include llm; must cover all tools' effects |
guard | Check function can have its own needs |
tool | Effects propagate to callers |
stream | Effects propagate to consumers |
server | Must include net; must cover all handlers' effects |