graded

Package Version Hex Docs CI

Effect checking for Gleam.

graded verifies that your Gleam functions respect their declared effect budgets. The tool reads and writes a single spec file at the root of your package — your Gleam source stays untouched.

Quick start

gleam add --dev graded

Infer effects for your project:

gleam run -m graded infer

This scans src/, analyses every function, and writes two outputs:

Example

In a Lustre app, view must be pure — it builds HTML from the model without side effects. Enforce this with graded:

// src/app.gleam
import gleam/io
import lustre/element.{type Element}
import lustre/element/html

pub fn view(model: Model) -> Element(Msg) {
  io.println("rendering")  // oops — side effect in view!
  html.div([], [html.text(model.name)])
}
// app.graded — at the project root
check app.view : []
$ gleam run -m graded check
src/app.gleam: view calls gleam/io.println with effects [Stdout] but declared []

graded: 1 violation(s) found

Remove the io.println and the check passes. Lustre’s init and update functions are also pure — they return #(Model, Effect(Msg)) where Effect is a data description, not an executed side effect.

Function names in the spec file are module-qualified: app.view means the view function in module app. Use slashes for nested module paths (app/router.handle_request).

Configuration

graded reads its configuration from a [tools.graded] table in gleam.toml. Both fields are optional — omit them to get the defaults.

[tools.graded]
spec_file = "myapp.graded"      # default: "<package_name>.graded"
cache_dir = "build/.graded"     # default: "build/.graded"

Publishing your spec file to consumers

If you’re a library author and want downstream packages to read your effect annotations, add the spec file to included_files in your gleam.toml:

included_files = [
  "src",
  "myapp.graded",        # ← add this so consumers see your effects
  "gleam.toml",
  "README.md",
]

The cache directory under build/ is gitignored and never ships, regardless of included_files.

Reference

The .graded spec language and graded’s analysis model are documented in full in docs/REFERENCE.md — the annotation kinds (effects, check, type, external effects, returns), effect-set syntax, effect resolution order, higher-order and second-order effect polymorphism, type field effects, the effect-label conventions, and the bundled catalog of common packages.

Commands

gleam run -m graded check [directory]         # enforce check annotations (default)
gleam run -m graded infer [directory]         # infer and write effects annotations
gleam run -m graded format [directory]        # normalize .graded file formatting
gleam run -m graded format --check [directory] # verify formatting (CI mode)
gleam run -m graded format --stdin            # format from stdin (editor integration)

Limitations

graded is sound, not complete: it combines syntax-level analysis (glance) with type information (girard), and when it can’t statically trace a function value it falls back to the [Unknown] effect rather than guess. [Unknown] fails an effect budget, so graded never silently understates effects — but a few value-flow patterns need a hand-written annotation or a wider budget to resolve.

Idiomatic Gleam — inline callbacks, direct and aliased function references, pipe chains, higher-order functions passing functions by name (including second-order operator effects), and validator/handler/config records — is handled automatically, including across modules: a fresh checkout resolves transitive chains with no prior graded infer (committed effects lines always win, and check writes nothing to disk).

The handful of patterns that fall back to [Unknown] — each with how it shows up and how to work around it — are documented in docs/LIMITATIONS.md.

License

Apache-2.0

Search Document