Step 8. Effects
Why#
If a function is called total(items), we expect it to compute a sum. But may it also go to the network, read a file
or sleep for a second? In most languages you cannot know without reading the body. In OxL you see it in the signature:
| Effect | Allows |
|---|---|
io | files, processes, environment, stdout/stderr |
net | the network: rpc, HTTP, actors, cluster |
db | ORM, std.kv, the built-in storage |
time | reading the clock, sleep, timers |
rand | random numbers |
pure | none of the above |
A function may call only functions whose effects are part of its own set. That makes pure functions easy to test
(the result depends only on the arguments), and suspicious places where something "leaks" are found by the compiler. The same effects
tell the scheduler that a function can be suspended, which is why OxL has no async/await and no function "colors".
Example#
fn double(x: i32) -> i32 pure => x * 2 // pure: no I/O and no clock
fn log(msg: str) io { // io: writes to stdout
print("[log] {msg}")
}
fn timed(x: i32) -> i32 io, time { // several effects, comma separated
log("sleeping")
sleep(5ms)
double(x) // a pure function may be called from anywhere
}
fn main() io, time {
print("{timed(21)}")
}[log] sleeping 42
Walkthrough#
doubleispure: it can be called from anywhere, including otherpurefunctions.logdeclaresio(the result type is omitted in the-> ()form).timedlistsio, time: inside it calls bothlogandsleep.mainmust declareio, timetoo.- For private functions effects may be omitted - the compiler infers them. For
pub(api)functions effects are mandatory: they are part of the public API, and changing effects is a breaking change. - A panic is not an effect: it is possible in any function.
Common mistake: an undeclared effect#
fn calc() -> i32 pure {
sleep(1s) // sleep requires the time effect
42
}effects
effects: calc is declared pure but calls sleep, which needs time. There are two fixes:
add the effect to the signature (-> i32 time) and declare it in all callers - or remove sleep from the function that must
stay pure. The second is usually better: keep the business logic pure and the I/O on the edges of the program.
Remember#
- Effects are part of the function type;
puremeans no effects. - The caller must "allow" all effects of the callee.
- A pure core plus a thin shell with effects is the natural architecture in OxL.
Next: modules and packages.