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).
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:
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 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) |
| AI | TokenCount, TokenUsage, ChatMessage, ChatRole, Vector, Embedding |
ai (AI) |
| 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:
| 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:
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 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), ormoney.from_minorforMoney), and from text withtext.parse(raw, as: T). Each failure is named by the rule it breaks, andmalformedmeans the text isn't that type at all. - Show and write.
text.format(value, locale:)gives text for people, andtext.from(value)gives the canonical text (E.164, ISO codes, ISO 8601) thattext.parsereads back. A type with no single text form, such asPostalAddress, 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.writeand every route. - Equality and order.
==works on every type;<andsort_bywork where order means something (quantities of one kind,Moneyof 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
with a reason. A declaration with the same name
as a standard type is type.standard_clash. Both are explained under
The standard types and
A release that adds a standard type.
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.
A sum of lengths can mix units, and the result is a Length. Inches are
written inch, because m means minutes:
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%)}
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:
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%)}
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_operandcheck 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):
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))}
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:
type CakePrice = Money { currency: Currency.gbp}/// Twenty percent added to a price: ambiguous, so refused.function more(price: CakePrice) -> CakePrice { return CakePrice(price + 20%)}
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_operandcheck failed: 1 error, 1 file
Adding two amounts whose types are in different currencies is a compile error:
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)}
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_operandcheck 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:
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)}
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). |
| 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). |
| 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). |
| Services | SearchText, OAuthGrant, AnyObject |
What people type into a search box (Search); 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); 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), Event (Declaring an event), EmailMessage (Email), Document, a template rendered to HTML or PDF (Documents), and Message, a translated message whose fields are its placeholders (Translations as messages). |
| 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 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); 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.
Markdown for agents: /docs/built-in-types.md.Built from Jadpo main at a707efb.