Blog

Saying what a function may touch

2 min read

A function's signature says what goes in and what comes out, but not whether it writes to the database on the way. A short `with` clause can.

Reading a web codebase, the hardest question to answer is often the simplest: does this function talk to the database? Call the network? Read the clock? The signature does not say, and the answer can be three calls deep.

That matters more on the web than elsewhere. A function that only computes can run anywhere: in a test, in the browser, twice. One that writes cannot.

Effects in the signature

fn load_user(id: int) with db = db.one("select * from users where id = ?", id)fn fetch_avatar(url: str) with http = http.get(url).bodyfn profile(id: int) with db, http = { user: load_user(id), avatar: fetch_avatar(avatar_url(id)) }

with db declares that the function may use the database, and everything it calls is held to the same list. A function with no with that reaches for db.*, directly or through a helper, is a mistake the checker reports before the program runs.

What it buys

The list is short on purpose: the database, the network, files, the environment, time and randomness. Enough to answer the useful questions, few enough that nobody has to look them up.

Since then: with clauses are checked by quillo check, and the same idea decides what a component may run in the browser. See Effects.

The Quillo newsletterNew posts, components and plugins, about once a week. No tracking.

Thread
6Useful? Like it, or add to its thread below.

Thread 0 posts

On Saying what a function may touch: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.