# Built-in types

Jadpo comes with its types built in. You use them without declaring or
importing anything, and you can't reuse their names for your own types. There
are three groups: the six representation types, the standard types, and a few
types the language's own features need.

## Representation types

The representation types say how a value is held: as a number, as text, as
bytes. They are the raw material you build your own types from.

| Type | What it holds |
|---|---|
| `Bool` | `true` or `false` |
| `Int` | An exact 64-bit integer |
| `Decimal` | An exact decimal with a required `scale`, the number of decimal places it keeps |
| `Text` | Unicode text |
| `Bytes` | A sequence of bytes |
| `Uuid` | A universally unique identifier |

There is no `Float`. Arithmetic on `Decimal` is exact, and its other
operations are under `decimal.` ([Numbers](numbers.md)).

Text that arrives from outside, such as a request body, is put into Unicode's
NFC form as it is read, before its rules are checked. NFC writes each
character one way, so the same text always compares equal.

By default a `Uuid` accepts any UUID version and is kept in lowercase. The IDs
Jadpo generates for new records are version 7. A named type can fix the
version, which decides both what it accepts and what it generates:

```jadpo
entity Order {
    id: Uuid  // generated as version 7
}

type PartnerRef = Uuid {
    version: UuidVersion.v4  // accepts and generates only version 4
}
```

## Standard types

The standard types are one shared definition each of the things most apps
need: a postal address, an amount of money, a length, a point on the earth.
Without them, every app and every package would invent its own. Each has one
way to build it that checks its rules, one JSON shape and one storage shape.
Its fields are named, never positional:
`Point { latitude: ... longitude: ... }`.

Their names are global, like `Int`, so you never import one. Their
operations are grouped under a prefix such as `money.`, `units.` or
`temporal.`. There are 253 standard types, and
[The standard types](the-standard-types.md) lists every
one and every operation. The groups and their best-known types are:

| Group | Headline types | Namespaces |
|---|---|---|
| Money and payments | `Money`, `Currency`, `ExchangeRate`, `Price`, `TaxRate`, `Discount`, `Iban`, `BankAccount`, `PaymentCardNumber` | `money` |
| Time and calendar | `Instant`, `CalendarDate`, `ZonedTime`, `Duration`, `LocalTime`, `DateRange`, `RecurrenceRule`, `OpeningHours` | `temporal`, `range` |
| Quantities and units | `Length`, `Mass`, `Temperature`, `Speed`, `Energy`, `DataSize`, `Percentage`, `Ratio`, `Range<T>` | `units`, `range` |
| People and places | `PersonName`, `Username`, `Email`, `PhoneNumber`, `PostalAddress`, `PostalCode`, `Country`, `Language`, `Locale`, `Zone` | `locale`, `text` |
| Web and network | `Url`, `Slug`, `DomainName`, `HttpStatus`, `IpAddress`, `Cidr`, `Cookie` | `web` |
| Software | `SemanticVersion`, `GitRef`, `FilePath`, `TraceId`, `Ulid`, `IdempotencyKey` | `semver` |
| Text and documents | `Markdown`, `Html`, `RichText`, `LocalisedText` | `markup`, `text` |
| Media | `Colour`, `ImageDimensions`, `AspectRatio`, `FrameRate`, `VideoCodec`, `Timecode`, `ContentRating` | `colour`, `media` |
| Security and secrets | `Password`, `PasswordHash`, `Hash`, `Hmac`, `Jwt`, `PublicKey`, `Certificate`, `ApiKey`, `SecretText`, `EncryptedValue` | `crypto`, `hash`, `random` |
| Geospatial | `Point`, `Latitude`, `Polygon`, `Geometry`, `BoundingBox`, `Geohash` | `geo` ([Geospatial](geospatial.md)) |
| AI | `TokenCount`, `TokenUsage`, `ChatMessage`, `ChatRole`, `Vector`, `Embedding` | `ai` ([AI](ai.md)) |
| Codes and identifiers | `Sku`, `Ean13`, `Isbn`, `Barcode`, `TrackingNumber`, `Vin`, `VatNumber`, `NhsNumber`, `Icd10Code`, `IataAirport`, `FlightNumber`, `Orcid` | none beyond the contract below |

Every standard type, by group. "(enum)" marks a fixed list of values,
held as a built-in [enum](enums.md):

| Group | Types |
|---|---|
| People and contact (12) | `PersonName`, `GivenName`, `FamilyName`, `Username`, `Email`, `EmailWithName`, `PhoneNumber`, `PhoneExtension`, `PostalAddress`, `PostalCode`, `Uprn`, `Nationality` (enum) |
| Places (10) | `Country` (enum), `CountrySubdivision` (enum), `Continent` (enum), `UnLocode`, `CallingCode`, `Language` (enum), `WritingScript` (enum), `TextDirection` (enum), `Locale` (enum), `Zone` (enum) |
| Geospatial (17) | `Point`, `Latitude`, `Longitude`, `LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, `MultiPolygon`, `Geometry`, `BoundingBox`, `Circle`, `Bearing`, `Crs`, `EastingNorthing`, `MapTile`, `H3Cell`, `Geohash` |
| Money, payments, banking (24) | `Money`, `Currency` (enum), `ExchangeRate`, `Price`, `UnitPrice`, `TaxRate`, `Discount`, `BasisPoints`, `InterestRate`, `PaymentCardNumber`, `MaskedCardNumber`, `CardBrand` (enum), `CardBin`, `CardExpiry`, `PaymentReference`, `InvoiceNumber`, `Iban`, `Bic`, `UkSortCode`, `UkAccountNumber`, `BankAccount`, `AbaRoutingNumber`, `Lei`, `Isin` |
| Tax and identity numbers (12) | `VatNumber`, `TaxId`, `UkUtr`, `UkCompanyNumber`, `UkNationalInsuranceNumber`, `UsSsn`, `UsEin`, `PassportNumber`, `DrivingLicenceNumber`, `IdentityDocument`, `IdentityDocumentKind` (enum), `NhsNumber` |
| Commerce and logistics (22) | `Sku`, `Ean13`, `UpcA`, `Isbn`, `Sscc`, `Barcode`, `UnitQuantity`, `Rating`, `ClothingSize`, `ClothingSizeSystem` (enum), `ShoeSize`, `ShoeSizeSystem` (enum), `Dimensions`, `HsCode`, `Incoterm` (enum), `TrackingNumber`, `Carrier`, `ContainerNumber`, `LotNumber`, `SerialNumber`, `Vin`, `VehicleRegistration` |
| Time and calendar (19) | `Instant`, `CalendarDate`, `ZonedTime`, `Duration`, `Weekday` (enum), `LocalTime`, `LocalDateTime`, `Month` (enum), `YearMonth`, `Year`, `Quarter`, `IsoWeek`, `UtcOffset`, `DateRange`, `InstantRange`, `CalendarPeriod`, `RecurrenceRule`, `OpeningHours`, `CalendarEvent` |
| Physical quantities (17) | `Length`, `Area`, `Volume`, `Angle`, `Mass`, `Pressure`, `Energy`, `Power`, `Temperature`, `TemperatureDifference`, `Speed`, `Frequency`, `Voltage`, `Current`, `Illuminance`, `SoundLevel`, `Concentration` |
| Maths and data (8) | `Percentage`, `Ratio`, `Fraction`, `Range<T>`, `DataSize`, `Bitrate`, `Vector`, `Embedding` |
| Health (10) | `BloodType` (enum), `Icd10Code`, `SnomedConcept`, `Dose`, `DosageInstruction`, `RouteOfAdministration` (enum), `HeartRate`, `BloodPressure`, `BloodGlucose`, `Allergen` (enum) |
| Media and visual (16) | `Colour`, `ImageDimensions`, `AspectRatio`, `Rect`, `Insets`, `PaperSize` (enum), `ExifOrientation` (enum), `AltText`, `FrameRate`, `VideoResolution`, `VideoCodec`, `AudioCodec`, `Timecode`, `SubtitleCue`, `ContentRating`, `ContentRatingScheme` (enum) |
| Web and network (23) | `Url`, `UrlPath`, `RelativeUrl`, `Slug`, `DomainName`, `Hostname`, `WebOrigin`, `HttpMethod` (enum), `HttpStatus`, `HttpHeaderName`, `FileType`, `UserAgent`, `ProblemDetails`, `JsonPointer`, `Cookie`, `UtmParameters`, `IpAddress`, `Cidr`, `NetworkPort`, `MacAddress`, `Imei`, `Emoji`, `PushToken` |
| Software (19) | `SemanticVersion`, `VersionRange`, `GitRef`, `PackageUrl`, `Cve`, `SpdxLicence`, `TraceId`, `SpanId`, `TraceParent`, `LogLevel` (enum), `FilePath`, `FileName`, `RegularExpression`, `ContainerImage`, `Digest`, `Uuid`, `Ulid`, `IdempotencyKey`, `Doi` |
| Text formats (4) | `Markdown`, `Html`, `RichText`, `LocalisedText` |
| Security (20) | `Password`, `PasswordHash`, `PasswordPolicy`, `Hash`, `Hmac`, `Jwt`, `JwtClaims`, `Jwk`, `PublicKey`, `PrivateKey`, `Certificate`, `TotpSecret`, `OneTimeCode`, `ApiKey`, `SecretText`, `BearerToken`, `OAuthScope`, `OidcClaims`, `WebAuthnCredential`, `EncryptedValue` |
| Travel (6) | `IataAirport`, `IcaoAirport`, `IataAirline`, `FlightNumber`, `CabinClass` (enum), `BookingReference` |
| Other with a published standard (10) | `UnNumber`, `Orcid`, `CefrLevel` (enum), `Mpan`, `Mprn`, `EpcRating` (enum), `Tenure` (enum), `LawfulBasis` (enum), `Jurisdiction`, `ModelId` |
| AI (4) | `TokenCount`, `TokenUsage`, `ChatMessage`, `ChatRole` (enum) |

The money types have a few rules of their own, because money is where
mistakes cost most. `Money` holds a whole number of a currency's minor units,
such as pence or cents, and its `Currency`. It never holds a decimal number of
pounds. A type built on `Money` takes `currency:`, `rounding:`, and `min:` and
`max:` in minor units. The `money.` operations (`money.from_minor`,
`money.minor`, `money.sum`, `money.allocate`) build amounts and work them out.
A named type for an amount in pounds:

```jadpo
type Sterling = Money {
    currency: Currency.gbp
    min: 0
    rounding: Rounding.half_even
}
```

An amount in another currency doesn't compile where a `Sterling` is wanted.
Use `Int` for counts and sizes, and `Money` for amounts of money.
[Money and payments](money-and-payments.md) has the rest.

### What every standard type does

Every standard type meets the same contract, so one operation works across all
of them:

- **Construct and validate.** From its parts (`PostalCode(raw)`, or
  `money.from_minor` for `Money`), and from text with
  `text.parse(raw, as: T)`. Each failure is named by the rule it breaks, and
  `malformed` means the text isn't that type at all.
- **Show and write.** `text.format(value, locale:)` gives text for people, and
  `text.from(value)` gives the canonical text (E.164, ISO codes, ISO 8601)
  that `text.parse` reads back. A type with no single text form, such as
  `PostalAddress`, can't be asked for one.
- **Storage.** One shape: a column, or named columns for an object (`Money`'s
  two).
- **JSON.** One shape, read and written by `json.read`, `json.write` and every
  route.
- **Equality and order.** `==` works on every type; `<` and `sort_by` work
  where order means something (quantities of one kind, `Money` of one
  currency, times, `SemanticVersion`, `Percentage`).

A type that copies a standard type under another name is the error
`type.standard_exists`. For example, a `Booking` with only a start and an
end, each an `Instant`, is an `InstantRange`. When the copy is deliberate,
you can [decline the error](declining-a-judgement-call.md)
with a reason. A declaration with the same name
as a standard type is `type.standard_clash`. Both are explained under
[The standard types](the-standard-types.md) and
[A release that adds a standard type](standard-type-clashes.md).

## What the standard types do

Standard types come with their own arithmetic, units and rules, so you
write a sum as you would on paper and it is checked as strictly as a call.
Which operators work on which types is set by the
[standard operator table](the-standard-operator-table.md).

A sum of lengths can mix units, and the result is a `Length`. Inches are
written `inch`, because `m` means minutes:

```jadpo
type Shelf = Length {}

/// A metre, 2.3 inches and 3 centimetres, then 20% of that: a `Length`.
function fifth_of(base: Shelf) -> Shelf {
    return Shelf((base + 2.3inch + 3cm) * 20%)
}
```

```jadpo
test "units mix within a kind" {
    assert fifth_of(Shelf(1metre)) == Shelf((1metre + 2.3inch + 3cm) * 20%)
}
```

Mix up a unit and the compiler says so. Here `1m` is one minute, a
`Duration`, which can't be added to a `Length`:

```jadpo
type Shelf = Length {}

/// `m` is minutes, so this adds a `Duration` to a `Length`.
function mistaken(base: Shelf) -> Shelf {
    return Shelf((1m + 2.3inch + 3cm) * 20%)
}
```

```text
error: A duration and a quantity can't be added: a `Duration` and a `Length` are different kinds
 --> main.jadpo:5:19
  |
5 |     return Shelf((1m + 2.3inch + 3cm) * 20%)
  |                   ^^^^^^^^^^^^ different kinds
  reason: `+` works within one kind of quantity, in any of its units; a `Duration` and a `Length` are different kinds, so the sum means nothing.
  next: Use values of one kind, or a product or quotient the table defines (agent)
  rule: type.arithmetic_operand
  help: https://jadpo.dev/docs/errors/type.arithmetic_operand

check failed: 1 error, 1 file
```

Money is a whole number of minor units in one currency, so the operator
table allows less on it. A percentage scales it: `price * 120%` adds 20% VAT.
To share an amount out, use `money.split`, which loses no penny: £10.00 three
ways is £3.34, £3.33 and £3.33. Dividing, as in `price / 3`, is refused with
`type.money_split`, which suggests `money.split`
([Money operations](money-operations.md)):

```jadpo
type CakePrice = Money {
    currency: Currency.gbp
    min: 0
}

type Payment = Money {}

/// A price with 20% VAT added.
function with_vat(price: CakePrice) -> CakePrice {
    return CakePrice(price * 120%)
}

/// A bill shared three ways, every penny to someone.
function shares(total: CakePrice) -> List<Payment> {
    return list.map(money.split(total, 3), share -> Payment(share))
}
```

```jadpo
test "VAT and sharing lose nothing" {
    var price: CakePrice = CakePrice(money.from_minor(1000, Currency.gbp))
    assert with_vat(price) == money.from_minor(1200, Currency.gbp)
    assert shares(price) == [
        Payment(money.from_minor(334, Currency.gbp)),
        Payment(money.from_minor(333, Currency.gbp)),
        Payment(money.from_minor(333, Currency.gbp))
    ]
}
```

`price + 20%` could mean two things, so it is refused. Write `price * 120%`
instead:

```jadpo
type CakePrice = Money {
    currency: Currency.gbp
}

/// Twenty percent added to a price: ambiguous, so refused.
function more(price: CakePrice) -> CakePrice {
    return CakePrice(price + 20%)
}
```

```text
error: `CakePrice` and a percentage can't be added: it could mean two things
 --> main.jadpo:7:22
  |
7 |     return CakePrice(price + 20%)
  |                      ^^^^^^^^^^^ an amount and a percentage
  reason: `price + 20%` could mean the amount raised by 20% of itself, or 20% of some other amount; the standard operator table refuses it rather than guess. A percentage scales money by `*`.
  next: Write `price * 120%`, or `price + price * 20%`, whichever is meant (agent)
  rule: type.arithmetic_operand
  help: https://jadpo.dev/docs/errors/type.arithmetic_operand

check failed: 1 error, 1 file
```

Adding two amounts whose types are in different currencies is a compile
error:

```jadpo
type CakePrice = Money {
    currency: Currency.gbp
}

type EuroPrice = Money {
    currency: Currency.eur
}

/// A cake in pounds and a delivery charged in euros.
function delivered(price: CakePrice, delivery: EuroPrice) -> CakePrice {
    return CakePrice(price + delivery)
}
```

```text
error: `CakePrice` and `EuroPrice` can't be added: they're in different currencies
 --> main.jadpo:11:22
   |
11 |     return CakePrice(price + delivery)
   |                      ^^^^^^^^^^^^^^^^ GBP and EUR
  reason: `CakePrice` is in GBP and `EuroPrice` in EUR, as their types or the calls that made them fix; amounts are added only in one currency.
  next: Use two amounts in the same currency (agent)
  rule: type.arithmetic_operand
  help: https://jadpo.dev/docs/errors/type.arithmetic_operand

check failed: 1 error, 1 file
```

Dates, times and temperatures work the same way. A `CalendarDate` plus a
one-month `CalendarPeriod` stops at the last day of the month when the day
doesn't exist there. `Instant - Instant` gives a `Duration`. Subtracting one
temperature from another gives a `TemperatureDifference`:

```jadpo
type DeliveryDay = CalendarDate {}

type SeenAt = Instant {}

type Gap = Duration {}

/// A day a month on: the 31st of January gives the last day of February.
function month_on(day: DeliveryDay) -> DeliveryDay {
    return DeliveryDay(
        day + CalendarPeriod {
            years: 0
            months: 1
            days: 0
        }
    )
}

/// How long between two moments.
function between(start: SeenAt, end: SeenAt) -> Gap {
    return Gap(end - start)
}
```

```jadpo
test "calendar and clock arithmetic" {
    assert month_on(DeliveryDay("2026-01-31")) == DeliveryDay("2026-02-28")
    assert between(SeenAt("2026-10-25T09:00:00Z"), SeenAt("2026-10-25T11:30:00Z")) == Gap(150m)
    assert 25degC - 20degC == units.of(5, TemperatureDifferenceUnit.kelvin)
}
```

## Other built-in types

These types belong to features of the language. Each row links the page
that covers the feature.

| Kind | Types | Notes |
|---|---|---|
| Containers | `List<T>`, `Set<T>`, `Map<K, V>` | Described below. |
| Translation | `PluralCount`, `TextDirection`, `TranslationReview` | `PluralCount` is a whole number that picks a message's plural form, used directly as a `Message` field; `TextDirection` is a locale's direction, `.left_to_right` or `.right_to_left`, which `locale.direction(value)` gives; `TranslationReview` is `.none` or `.required`, the `review:` of `translations:` in `application { }` ([What the compiler checks of translations](what-the-compiler-checks-of-translations.md)). |
| Files | `File`, `FileUpload`, `UploadSlot`, `FileContents` | A stored file (its key, name, size and type) as an entity field; the token of a finished upload in an `Input`; the signed address, or addresses for each part, an upload goes to; and what `store.read` returns, a file's bytes for passing on (to a provider, a PDF service, an email), which the format helpers do not accept ([Files](files.md)). |
| Batches | `BatchRun`, `BatchProgress`, `RowProblem`, `ImportSummary`, `ExportSummary`, `CsvFormat` | A batch run, as the batch helpers return it and as an entity field can store it; its progress from `batches.status`; one bad row or failed item, without its value; what `finished:` receives after reading (or `batches.each`) and after writing, the latter holding the stored `File`; and who a CSV is written for, `CsvFormat.spreadsheet` or `.standard` ([Batches](batches-work-over-many-items.md)). |
| Services | `SearchText`, `OAuthGrant`, `AnyObject` | What people type into a search box ([Search](search.md)); a connection's credential held for each owner record, `OAuthGrant { owner: ... }`, written only in a connection's `credential:` and never readable by code ([Acting for each user: OAuth grants](acting-for-each-user-oauth-grants.md)); and, only as a package operation's input field, any of the app's object types, sent in its JSON form (Postmark's template variables, Stripe's metadata). |
| Bases | `Object`, `Enum`, `Input`, `Output` | Used only after `=` in a `type` declaration. Other features add bases the same way: `Failure` ([Failures](failures.md)), `Event` ([Declaring an event](declaring-an-event.md)), `EmailMessage` ([Email](email.md)), `Document`, a template rendered to HTML or PDF ([Documents](documents.md)), and `Message`, a translated message whose fields are its placeholders ([Translations as messages](translations-as-messages.md)). |
| Decoding | `DecodeProblem` | `field: Text`, a field's path with list positions as numbers, and `rule: Text`; never the value. It is what a failure mapped from `json.read`'s `mismatch` receives in its `problem` field, and only the runtime builds one. |
| Nothing | `Unit` | What an action returns when it returns nothing. |

The batch types' fields, as [Batches](batches-work-over-many-items.md)
uses them:

| Type | Fields |
|---|---|
| `BatchRun` | `id: Uuid`; storable in an entity field, so an app can keep the run on its own import record |
| `BatchProgress` | `items_done: Int`, `estimated_total: Int?` (from the file's size or the query's count), `problems: Int` (so far), `state: BatchState` (`.running`, `.finished`, `.failed`, `.cancelled`) |
| `RowProblem` | `row: Int` (as a spreadsheet numbers it), `column: Text`, `rule: Text` (the rule's code, as a refused request's problems give it, [HTTP](http.md)); never the value |
| `ImportSummary` | `items: Int`, `items_with_problems: Int`, `problems: List<RowProblem>` |
| `ExportSummary` | The same three fields, and `file: File`, the file written |

The literals, values written straight into code, are `true` and `false`,
whole numbers (`25`, `-3`), decimals (`1.25`), strings (`"Hello"`, with
`\"`, `\\`, `\n`, `\r` and `\t` escapes) and `none`.
