Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Standard library

Standard library

Groups with the baseline only

On this page

Many standard types are a value with a check and nothing more: an IBAN, a card number, a barcode, an NHS number. They have no operations of their own. They do what every standard type does (Every standard type meets one contract): each is built and checked from text or from its parts, sent, stored and shown to people. Their rules are how they are built, which text.parse and the type's data entry already cover.

Here a marketplace pays its sellers into the account they type, takes a patient's NHS number from a referral, and shows a card on file by its last four digits:

example
type FormText = Text {}type NotAnIban = Failure {    kind: FailureKind.invalid_value}type NotAnNhsNumber = Failure {    kind: FailureKind.invalid_value}/// The account a seller is paid into, as they typed it: checked by its/// country's length and its mod-97 check digits.function payout_account(raw: FormText) fails NotAnIban -> Iban {    var account: Iban = attempt Iban(raw) {        format: NotAnIban    }    return account}/// A patient's NHS number, as a referral sends it.function patient_number(raw: FormText) fails NotAnNhsNumber -> NhsNumber {    var number: NhsNumber = attempt text.parse(raw, as: NhsNumber) {        malformed: NotAnNhsNumber    }    return number}/// An account as a statement prints it.function account_label(account: Iban, viewer: Locale) -> PresentationText {    return text.format(account, locale: viewer)}/// A card on file as a receipt shows it: its last four only.function card_label(card: PaymentCardNumber, viewer: Locale) -> PresentationText {    return text.format(card, locale: viewer)}
example
test "payment and identity numbers are checked as they are built" {    var account: Iban = attempt payout_account("GB29NWBK60161331926819")    assert account == Iban("GB29NWBK60161331926819")    assert payout_account("GB29NWBK60161331926818") fails NotAnIban    assert payout_account("GB29 NWBK 6016 1331 9268 19") fails NotAnIban    var patient: NhsNumber = attempt patient_number("9434765919")    assert patient == NhsNumber("9434765919")    assert patient_number("9434765918") fails NotAnNhsNumber}test "they are shown as they are printed" {    var account: Iban = Iban("GB29NWBK60161331926819")    assert account_label(account, Locale.en_gb) == "GB29 NWBK 6016 1331 9268 19"    assert card_label(PaymentCardNumber("4242424242424242"), Locale.en_gb) == "•••• 4242"    assert text.format(NhsNumber("9434765919"), locale: Locale.en_gb) == "943 476 5919"}

Checks beyond a pattern

Most of these types are a pattern:. Where a rule is more than a pattern can say, such as a check digit, the type names one validator:

Group Validators beyond a pattern
People and contact PhoneNumber (E.164 per country), PostalCode (per country), PostalAddress and PersonName (CLDR order for text.format)
Places CountrySubdivision against ISO 3166-2, UnLocode by its pattern and its country, its list not shipped
Payment and identity numbers Iban (mod-97), PaymentCardNumber (Luhn), UkSortCode with UkAccountNumber (modulus check), AbaRoutingNumber, Lei, Isin, NhsNumber (mod-11), UkNationalInsuranceNumber (prefix rules)
Tax and identity documents VatNumber, TaxId, PassportNumber, DrivingLicenceNumber and IdentityDocument, each number by its country's published form and check digit (VIES, the OECD's and the EU's TIN pages, each issuer's own), by length where a country publishes none
Commerce and logistics Ean13, UpcA, Isbn, Sscc (GS1 check digits), Vin (check digit, for North American manufacturers), ContainerNumber (ISO 6346)
Health SnomedConcept (Verhoeff); Icd10Code by pattern, its release not shipped
Travel IataAirport, IcaoAirport, IataAirline by pattern, their lists not shipped
Other Orcid (ISO 7064 MOD 11-2), Mpan (Elexon's check digit)

Payment and identity numbers

Seventeen payment and identity numbers are held as text: PaymentCardNumber, CardBin, PaymentReference, InvoiceNumber, Iban, Bic, UkSortCode, UkAccountNumber, AbaRoutingNumber, Lei, Isin, UkUtr, UkCompanyNumber, UkNationalInsuranceNumber, UsSsn, UsEin and NhsNumber.

Held as text. Each is a type built on Text that the standard library declares; its stub is public type Iban = Text {}. So everything Values and types says of text applies: construction, literals checked while compiling, attempt, JSON (a string), storage (a text column) and ==. Your own type can be built on one and add rules: type SterlingIban = Iban { pattern: "GB.*" }.

Its pattern, matched against the whole text, and its validator are one check. Text that fails it breaks format, as text that isn't an Email does, so there is no new rule name. text.parse fails only as malformed. text.from is type.mismatch, because the value is text already. None of them is ordered.

The canonical text is the electronic form, in upper case with no spaces or dashes: GB29NWBK60161331926819, the sort code 089999, the SSN 123456789 and the EIN 123456789. Only that form is read, as text.parse reads only canonical text everywhere, so a printed IBAN and lower case are refused. Strip a form's spaces before you build one, with text.replace. An SSN or an EIN with dashes is the printed form.

Shown to people in the printed form, whatever the locale:

  • an IBAN in groups of four;
  • a sort code in pairs with dashes, 08-99-99;
  • a National Insurance number in pairs, AB 12 34 56 C;
  • an NHS number in groups of 3, 3 and 4, 943 476 5919;
  • an EIN as 2 then 7 digits;
  • the others as their canonical text.

The runtime sees only text, so the checker gives text.format the name of the standard type the value is held as, as an argument you don't write.

Masked by text.format, to be safe. A PaymentCardNumber and a UsSsn are shown as their last four only, •••• 4242 and •••-••-6789. That is as much of a card number as PCI DSS lets a page show, and how the SSA and the IRS shorten an SSN. Code that must show the whole number has it as text already.

Personal data. Each type names the personal data category its values are in, whatever field holds them:

  • financial for a card number, an IBAN and a UK account number;
  • government_id for a National Insurance number, an SSN and a UTR;
  • health for an NHS number.

Personal data treats a field of such a type as marked with that category: hashed in logs, kept out of caches when the category is sensitive, and its reads recorded. Logs carry no values, only a failure's code and a fault, so nothing writes one in clear. The check of what reaches a model already reads the category. A failed assertion in jadpo test still shows the value it compared, which is test data.

The validators:

  • an IBAN's mod-97, with each country's length from SWIFT's IBAN Registry. A country the registry adds later is refused until a release adds it, as a new currency is;
  • an LEI's ISO 7064 mod 97-10;
  • a card number's Luhn check;
  • an ISIN's Luhn check over its letters as numbers;
  • an ABA routing number's 3-7-1 checksum;
  • an NHS number's mod-11. A check digit of 10 is never issued;
  • a National Insurance number's prefix rules, from HMRC's NIM39110, with a suffix of A to D only;
  • a BIC's country, its fifth and sixth letters, which must be a current ISO 3166-1 code as Country holds them, or Kosovo's user-assigned XK, which SWIFT issues BICs under as it does IBANs. So YU, which was withdrawn, is refused.

The rest are patterns: a card BIN of 6 or 8 digits; a payment reference of up to 18 of Bacs's characters; an invoice number of 1 to 200 characters with no control character and no space at either end; a sort code; an account number; a UTR; a company number; an EIN; and an SSN, with the areas, groups and serials the SSA never issues refused.

The UK modulus check needs a sort code and an account number together, so it belongs to BankAccount, under Bank accounts.

Test values are published ones where a scheme publishes them: SWIFT's registry IBANs, Stripe's test cards, the NHS Number standard's example and its 999 test range, Vocalink's modulus checking test cases and GLEIF's LEIs. Where a scheme publishes none, they are made up to the rules. Each type's entry says which.

Personal data's handling of these categories isn't built yet; see What is built.

Markdown for agents: /docs/groups-with-the-baseline-only.md.Built from Jadpo main at a707efb.