Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 5 Nov 2026. What's built · Install 0.3.0
jadpo
ReferenceProject layout and modules

LanguageProject layout and modules

Imports

On this page

With imports, the place every name comes from can be read from the file itself. Without them, a name in one file could be declared in that file, in its domain's types/, failures/ or entities/, in shared/, or in another domain, and you or an agent would have to search for it.

A use line names a folder, then the names it brings in. Here a checkout action uses a failure that has a file of its own in the same domain, which is one business area of your app.

domains/sales/failures/basket_empty.jadpo:

example
type BasketEmpty = Failure {    kind: FailureKind.rejected}

domains/sales/actions/check_out.jadpo:

example
use sales/failures { BasketEmpty }type BasketSize = Int {    min: 0}action check_out(items: BasketSize) fails BasketEmpty -> BasketSize {    if (items == 0) {        reject BasketEmpty    }    return items}

use sales/failures { BasketEmpty } says BasketEmpty is in domains/sales/failures/basket_empty.jadpo. BasketSize is declared in this file, so it needs no use line. Int, Failure and FailureKind are built in, so they don't either.

Names on a use line are written exactly as the code uses them, so finding a name on the page finds its import. A longer list, from domains/sales/actions/price_basket.jadpo:

example
use shared/types { Percent, Sterling }use shared/libraries { pricing }use sales/types { Basket, DiscountCode, Receipt }use sales/failures { BasketEmpty, CodeUnknown, DiscountTooLarge }use sales/actions { price_lines }use menu

The braces hold a list separated by commas, the only one in the language: every other block holds one name: value item per line.

What is imported

  • Every name not declared in the file is imported, your own domain's included. A name brought in from sales/types is in domains/sales/types/<name>.jadpo, and one from shared/types is in shared/types/<name>.jadpo. The path leaves out domains/, so shared, app, config, authentication, policy and flags can't name a domain.
  • What the project declares is imported, root files included. A connection in app.jadpo comes in as use app { ... }.
  • Built-ins are never imported. Built-in types (Text, Int, List<T>) and standard-library namespaces (list.count(...)) are fixed per language version, and a project can't declare them. An entity's generated operations (Todo.get(id)) come with the entity.
  • The generated namespaces (clock, config) are called through their names, as the standard library is, and need no import.
  • Calls into another domain keep the domain's name at the call. use menu brings in the domain, and its public actions, queries and fixtures are called as menu.stock_item(...), so a call that crosses domains shows on its own line. Another domain's public types and failures come in by folder like any other name: use menu/types { MenuItem }.
  • A test file sees everything its subject file declares without importing it, so stock_item.test.jadpo calls the loaf_stock helper in stock_item.jadpo directly. Anything else it uses is imported.
  • An entity's provided not-found failure (Reads and writes) comes in from the entity's folder: use planning/entities { Todo, TodoNotFound }.

So a name you read has one of three answers: it is on a use line, so it is in <folder>/<name>.jadpo; it is a built-in; or it is declared in this file.

The standard library's namespaces are list, set, map, range, integer, decimal, money, units, text, markup, bytes, hash, encoding, crypto, random, json, csv, tsv, json_lines, temporal, locale, colour, media, web, semver, geo and ai (Standard library). Each is reserved by semantic.standard_namespace_reserved, so nothing in a project can take its name. Fields and named arguments keep every word: note.text and set: are fine.

The formatter keeps imports

jadpo fmt, or the editor, adds a missing import and removes an unused one, one line per folder, sorted. Every name a file can import is unique where it can be imported: a private function or type within its own domain, anything importable across domains across the project. So the import for a name is never in doubt, and a forgotten one is fixed for you rather than being a compile failure. jadpo check still reports an import that names nothing, and one the file doesn't use.

Go to definition works from any use of a name, in a body, a signature or a use line, and opens its declaration in its .jadpo file.

Where the lines go

use lines open the file, below the comments that open it, up to a blank line. use is a reserved word, so nothing is named use.

jadpo fmt sorts them as the example above is:

  • names before domains;
  • from the root files, through shared/, to each domain's folders by domain name;
  • a domain's or shared/'s folders in the order the layout lists them (entities/, types/, failures/ and so on);
  • then the use <domain> lines.

The names in the braces are sorted, and a line too long for 100 columns puts one name per line.

What jadpo check reports

Each of these comes with the edit that fixes it:

  • import.missing: a name used without its import.
  • import.unknown: a line naming a folder, domain or name that isn't there. A name imported from the wrong folder gets the edit that moves it to the right one.
  • import.unused: an import the file doesn't use.
  • import.unneeded: an import of a built-in, of the file's own declaration, of its test subject's, or of its own domain.
  • import.duplicate: a name or folder written twice.

Using another domain's private name is still visibility.private_cross_domain where the file uses it. A call that reaches nothing is still reported by name resolution, as an unknown name or callee, not as an import problem: Jadpo never asks you to import something the file can't use.

jadpo fmt fixes imports, then formats, by applying those edits, once every file in the project parses. jadpo fmt --check fails on a file whose imports it would change.

Edge cases

  • A misspelling is one error. While a name the file writes names nothing, no import is called unused, since the name may be a misspelt import, and jadpo fmt removes none either.
  • Before a promotion. A declaration in a file named after another, such as SnoozeDays beside snooze_todo, comes in from the folder its file is in (use planning/actions { SnoozeDays }) while layout.promotion_required asks for the move. The move's edit makes it, and jadpo fmt then moves each import to the name's new folder. jadpo fmt --layout does both in one step.
  • Loose files have no layout, so there are no folders to import from. Every name in them is seen without a use line, and a use line there is import.unknown.
  • A migration's changes.jadpo, which jadpo migrate new writes and which names what changed (Todo.rank: { ... }), imports nothing either.

Markdown for agents: /docs/imports.md.Built from Jadpo main at aee2e8f.