Records and health measures
On this page
These types are the records that sit beside an app's own data: a parcel's size and tracking number, a flight, a clothing size, a vehicle registration, a rating, a barcode. With them are the health measures a clinic records: a pulse, a blood pressure, a glucose level, and a prescription's dose.
Here a courier labels each parcel with its size and its carrier's tracking number, a travel desk reads the flight a booking names, and a library reads the ten-digit ISBNs its old catalogue lists as the books' 13-digit ones:
type ScannedText = Text {}type NotABook = Failure { kind: FailureKind.invalid_value}type NotAFlight = Failure { kind: FailureKind.invalid_value}/// A book the old catalogue lists by its ISBN-10, read as its `Isbn`.function catalogued_book(raw: ScannedText) fails NotABook -> Isbn { var book: Isbn = attempt text.parse(raw, as: Isbn, form: CodeForm.isbn10) { malformed: NotABook } return book}/// The flight a booking names, as the airline writes it.function booked_flight(raw: ScannedText) fails NotAFlight -> FlightNumber { var ba_flight: FlightNumber = attempt text.parse(raw, as: FlightNumber) { malformed: NotAFlight } return ba_flight}/// A parcel's size as its label prints it.function parcel_size(shipped_box: Dimensions, packer: Locale) -> PresentationText { return text.format(shipped_box, locale: packer)}/// The same in inches, for a carrier that measures in them.function parcel_in_inches(shipped_box: Dimensions, packer: Locale) -> PresentationText { return text.format(shipped_box, locale: packer, unit: LengthUnit.inch)}
test "an ISBN-10 is read as its 13 digits" { var book: Isbn = attempt catalogued_book("0306406152") assert book == Isbn("9780306406157") var with_x: Isbn = attempt catalogued_book("080442957X") assert with_x == Isbn("9780804429573") assert catalogued_book("0306406153") fails NotABook assert catalogued_book("0-306-40615-2") fails NotABook}test "a flight is written one way" { var ba_flight: FlightNumber = attempt booked_flight("BA123") assert ba_flight.airline == IataAirline("BA") assert ba_flight.number == 123 assert ba_flight.suffix == none assert booked_flight("BA0123") fails NotAFlight assert booked_flight("BA 123") fails NotAFlight}test "a parcel is labelled in the carrier's units" { var shipped_box: Dimensions = Dimensions { length: 12inch width: 9inch height: 4inch } assert parcel_size(shipped_box, Locale.en_gb) == "30.48 × 22.86 × 10.16 cm" assert parcel_in_inches(shipped_box, Locale.en_us) == "12 × 9 × 4 in" var ups_label: TrackingNumber = TrackingNumber { carrier: "UPSN" number: "1Z999AA10123456784" } assert text.from(ups_label) == "UPSN 1Z999AA10123456784"}test "the other records are read and shown by their standards" { var size: ClothingSize = ClothingSize { system: ClothingSizeSystem.uk size: "10" } assert text.from(size) == "UK 10" var munich_plate: VehicleRegistration = VehicleRegistration { country: Country.de plate: "M-AB 1234" } assert text.from(munich_plate) == "DE M-AB 1234" var stars: Rating = Rating { value: 4.5 best: 5.0 worst: 1.0 } assert text.format(stars, locale: Locale.en_gb) == "4.5/5.0" var flour: UnitQuantity = UnitQuantity.mass { amount: 2.5kg } assert text.format(flour, locale: Locale.en_gb) == "2.5 kg" var scanned: Barcode = Barcode.ean13 { data: "4006381333931" } assert text.format(scanned, locale: Locale.en_gb) == "4006381333931" var applies_in: Jurisdiction = Jurisdiction.subdivision { subdivision: CountrySubdivision.gb_eng } assert text.format(applies_in, locale: Locale.en_gb) == "England"}
Here a clinic shows each reading as a clinician reads it, whatever unit a monitor sent it in, and keeps a prescription's instruction as its parts, never as free text to read back:
type MonitorText = Text {}type MeterReading = Decimal { scale: 1}type BadPulse = Failure { kind: FailureKind.invalid_value}type BadGlucose = Failure { kind: FailureKind.invalid_value}/// A pulse as a monitor sends it, `72 bpm`: no unit, the wrong kind and a/// pulse past 300 beats a minute each fail.function monitored_pulse(raw: MonitorText) fails BadPulse -> HeartRate { var pulse: HeartRate = attempt text.parse(raw, as: HeartRate) { malformed: BadPulse unknown_unit: BadPulse min: BadPulse max: BadPulse } return pulse}/// A glucose level as a UK meter gives it, in millimoles a litre to one/// place.function metered_glucose(meter_value: MeterReading) fails BadGlucose -> BloodGlucose { var metered_level: BloodGlucose = attempt BloodGlucose( units.of(meter_value, ConcentrationUnit.millimole_of_glucose_per_litre) ) { min: BadGlucose } return metered_level}/// A reading as the chart shows it.function pressure_shown(chart_reading: BloodPressure, viewer: Locale) -> PresentationText { return text.format(chart_reading, locale: viewer)}/// A glucose level in millimoles a litre, as a UK meter reads it.function glucose_shown(glucose_level: BloodGlucose, viewer: Locale) -> PresentationText { return text.format(glucose_level, locale: viewer)}/// The same in milligrams a decilitre, as a US laboratory reports it.function glucose_for_us_lab(glucose_level: BloodGlucose, viewer: Locale) -> PresentationText { return text.format( glucose_level, locale: viewer, unit: ConcentrationUnit.milligram_per_decilitre )}/// How to take a medicine, as the label prints it.function label_line(leaflet_dose: DosageInstruction, viewer: Locale) -> PresentationText { return text.format(leaflet_dose, locale: viewer)}
test "a pulse is read in either unit and shown in beats a minute" { var pulse: HeartRate = attempt monitored_pulse("72 bpm") var in_hertz: HeartRate = attempt monitored_pulse("1.2 Hz") assert pulse == in_hertz assert pulse == HeartRate(72bpm) assert text.format(pulse, locale: Locale.en_gb) == "72 bpm" assert text.from(pulse) == "1.2 Hz" assert monitored_pulse("72") fails BadPulse assert monitored_pulse("72 mmHg") fails BadPulse assert monitored_pulse("400 bpm") fails BadPulse}test "a reading is shown as a clinician reads it" { var at_rest: BloodPressure = BloodPressure { systolic: 120mmHg diastolic: 80mmHg } assert pressure_shown(at_rest, Locale.en_gb) == "120/80 mmHg" var in_kilopascals: BloodPressure = BloodPressure { systolic: 16kPa diastolic: 10.7kPa } assert pressure_shown(in_kilopascals, Locale.en_gb) == "120/80 mmHg" var fasting: BloodGlucose = attempt metered_glucose(5.5) assert glucose_shown(fasting, Locale.en_gb) == "5.5 mmol/L" assert glucose_for_us_lab(fasting, Locale.en_us) == "99 mg/dL" assert metered_glucose(-1.0) fails BadGlucose}test "an instruction is kept as its parts" { var twice_daily: DosageInstruction = DosageInstruction { dose: Dose.mass { amount: 500mg } route: RouteOfAdministration.oral times: 2 per: 24h as_needed: false } assert label_line(twice_daily, Locale.en_gb) == "500 mg, Oral use, 2 times per 1 day" var when_needed: DosageInstruction = DosageInstruction { dose: Dose.volume { amount: 10ml } route: RouteOfAdministration.oral times: 1 per: 6h as_needed: true } assert label_line( when_needed, Locale.en_gb ) == "10 ml, Oral use, 1 time per 6 hours, when needed"}
A reading written out with its pressures the wrong way round is refused while compiling, because the pressure between beats is always below the pressure as the heart beats:
/// A reading typed into the chart.function typed_reading() -> BloodPressure { return BloodPressure { systolic: 80mmHg diastolic: 120mmHg }}
error: This `BloodPressure` breaks its rule `diastolic_below_systolic` --> main.jadpo:5:20 |5 | diastolic: 120mmHg | ^^^^^^^ not below the systolic reason: A `BloodPressure`'s `diastolic`, the pressure between beats, is below its `systolic`, the pressure as the heart beats (LOINC 85354-9). A literal is checked while compiling. next: Write a `diastolic` below the `systolic`, or swap the two (agent) rule: type.invalid_literal help: https://jadpo.dev/docs/errors/type.invalid_literalcheck failed: 1 error, 1 file
Health measures
Three supporting enums go with these types: ClothingSizeSystem and
ShoeSizeSystem, the systems a size is in, and RouteOfAdministration,
the routes a dose is taken by.
Four units serve the measures:
PressureUnit.millimetre_of_mercury,mmHg, 133.322387415 Pa as CLDR converts it, with the literal suffix120mmHg;FrequencyUnit.beat_per_minute,bpm, 1/60 Hz, with the literal suffix72bpm;ConcentrationUnit.milligram_per_decilitre,mgdL, 0.01 g/L;ConcentrationUnit.millimole_of_glucose_per_litre, for glucose alone, whose code is its name: 0.1801557 g/L, by glucose's molar mass of 180.1557 g/mol, as CLDR converts itsmilligram-ofglucose-per-deciliter.
The two concentration units have no literal suffix, because theirs would
hold a /. Each unit is read by its code. Text and the wire still write
a quantity in its base unit.
The measures are quantities, each held in its kind's base unit:
HeartRateis aFrequencyfrom 0 to 300 beats a minute, shown in whole beats a minute. A frequency is held to the millihertz, so 73 bpm is held as 1.217 Hz and shown as 73 again.BloodPressureis asystolicand adiastolicPressure, neither below 0, the diastolic below the systolic. It is shown120/80 mmHg, in whole millimetres of mercury.BloodGlucoseis aConcentration, never below 0, shown in millimoles of glucose a litre to one place, or in whole milligrams a decilitre withunit:.
text.format takes unit: for each, and a value made in another unit,
such as 16kPa, is shown in the clinician's. They are a person's
health data (Personal data).
Doses and instructions
Dose is a choice of an amount by mass or by volume, at least a
microgram or a microlitre, the smallest step each kind holds. It is shown
in milligrams or millilitres exactly, so a microgram dose is never shown
in a unit a reader could take for a milligram. A count of tablets or of
units of insulin is your application's own type.
DosageInstruction is the commonest form of FHIR's Dosage, held as
parts, never as free text to read back: a dose, a route, times (at
least once) per a Duration above nothing, and as_needed. It is
shown in English, as a route's name is: 500 mg, Oral use, 2 times per 1 day. To show it in another language, word it in your app's own
messages.
A dose and an instruction describe a medicine, so neither is personal data. A patient's prescription is your application's record, which you mark.
RouteOfAdministration is the 14 routes of EDQM's Standard Terms
that a leaflet most often names. Each variant's code is its own name, and
it is shown as EDQM words it ("Oral use"). EDQM's numeric codes aren't
used.
Sizes
The size systems are the UK's, continental Europe's and the US's,
written UK, EU and US. Mondopoint (ISO 9407) and other national
systems come with a later release.
A size is the label in its system, not a measurement. A clothing size is
a number of up to three digits, the US's 00, or XXS to 5XL. A shoe
size is a whole or half size. Whose scale it is on, women's, men's or
children's, belongs to the product, as Google's and schema.org's product
data hold it. Each size is written as the label writes it, UK 10 or
EU 42, and stored as a column each.
Barcodes, amounts and ratings
Barcode is a choice by symbology:
ean13andupc_a, whose data is the code type of that name, with its GS1 check digit;code128, 1 to 80 printable ASCII characters;qr_code, 1 to 2,953 characters of ISO 8859-1 in byte mode, as many as a version 40 symbol holds.
It is sent tagged and shown as its data. Other symbologies come with a later release.
UnitQuantity is a choice of a count, each (UN/ECE Recommendation
20's EA), or an amount by mass, volume, length or area, the
kinds UnitPrice prices by. None is below 0. It is shown in its kind's
base unit, as UnitPrice is. Recommendation 20's list of trade units
isn't adopted.
Rating is schema.org's value, best and worst: the value is on
the scale, and the best is above the worst. It is held as written, so 4.5
of 5 and 9 of 10 are different values, and shown 4.5/5.
Rules across parts: a rating's best_above_worst and
value_on_scale, and a blood pressure's diastolic_below_systolic. Each
is proved while compiling for a literal that writes both parts as numbers,
or as quantities of one kind (type.invalid_literal). For a value built
in code from other values it is proved or attempted. It is checked as
JSON is read.
Parcels, vehicles and flights
Dimensions is a length, width and height, none below 0. It is
sent as named fields and stored as a column each. It is shown in
centimetres, or in the unit unit: names, with the unit written once:
30 × 20 × 10 cm. A parcel's weight is a Mass field of your own beside
it.
TrackingNumber is a Carrier and 6 to 40 capitals and digits,
which covers the large carriers' published forms. A carrier's own form
and check digit aren't checked.
VehicleRegistration is a Country and a mark of up to four groups
of capitals, letters of a script with no case, or digits, joined by a
space or a hyphen. It is kept as written, because a separator is part of
some marks, such as a German district's code.
A tracking number and a registration are each written as the code, a
space and the rest, and stored as a column each. Neither is personal
data, as a Vin isn't.
FlightNumber is IATA's SSIM designator: an IataAirline, a number
from 1 to 9999, and an optional capital suffix. It is written with no
space or leading zero, BA123, and stored as a column each. A flight on
a date is your application's record of a FlightNumber and a
CalendarDate.
Jurisdictions and models
Jurisdiction is a choice of a country or a subdivision, sent
tagged and shown by its CLDR name. Like every choice, it has no single
text form.
ModelId names an AI model: a provider and a name, written as
Hugging Face writes a repository, meta-llama/Llama-3.1-8B-Instruct. It
is kept as written, and the name may hold the : and @ of a version
tag. It is stored as a column each.
Text forms and ISBN-10
Text forms: TextForm::Joined writes two parts with a separator, the
first an enum's code, a standard type's text or a pattern's.
TextForm::FlightNumber writes IATA's designator.
ISBN-10: to read an old ten-digit ISBN, use
text.parse(raw, as: Isbn, form: CodeForm.isbn10). Like Country's
form:, it only reads. It takes nine digits and a check character, a
digit or X in either case, with no hyphens, and gives the ISBN-13: 978,
the first nine digits, and GS1's check digit. A wrong check character is
malformed. text.from never writes an ISBN-10: asking it to is
type.temporal_option_unused, as asking for a form a list doesn't have
is.
Markdown for agents: /docs/records-and-health-measures.md.Built from Jadpo main at a707efb.