Chapters
Types and formatting
Every value in a Catena sequence has a type — a decimal number, a whole number, a date, a length of time, a label, and so on. You rarely have to write the type down: Catena works it out from what you type. But knowing the small set of types, and why they exist, explains why money stays exact, why a date will not quietly turn into a number, and how the AS operator lets you both convert a value and format it for display.
This chapter teaches the type system first, then the literals you write to create values, then the two jobs of AS. If you are new to Catena, read Language basics first; this chapter builds on the arrow assignment (->) shown there.
The friendly type set
Catena presents a curated set of types with plain-English names. You will only ever meet these eight:
| Type | What it holds | Typical use |
|---|---|---|
Num |
An exact decimal number — the default | Money, prices, everyday arithmetic |
Real |
An approximate number that may carry a long fractional tail | Results of trigonometry, logarithms, and irrational roots |
Int |
A whole number | Counts, indexes, quantities of items |
Bool |
A truth value: TRUE or FALSE |
Yes/no conditions and comparisons |
Date |
A point in time | An invoice date, a deadline |
Duration |
A length of time, such as 30 days |
Gaps between dates, loan terms |
Text |
A label, a code, or typed-in prompt text | "Net price?", a currency code like "USD" |
Bag |
The one container — a list of values | A column of figures, [150, 200, 99.50] |
A value's type is fixed the first time you assign to it, and every later use of that name is treated as that type. This is why a simple typo cannot silently create a second variable: reading a name you never assigned is flagged as an error before the sequence runs, with a "did you mean...?" hint.
Two of these types deserve their own sections, because the difference between them is the most consequential idea in Catena: Num and Real.
Why money is exact by default
Type 0.10 + 0.20 into most computer languages and you get 0.30000000000000004. That is not a bug in the language; it is a limitation of binary floating point, which cannot represent tenths and hundredths precisely. For a calculator whose audience includes accountants, that is unacceptable — it silently corrupts money.
So Catena's default number type, Num, is exact decimal. It stores 0.10 as exactly one tenth, and:
- Number literals you type are
Num. - Adding, subtracting, multiplying, and dividing
Numvalues gives an exactNum.
That means money arithmetic is exact from end to end. A hundred line items summed as Num will not drift by a fraction of a cent.
Real is the other kind of number — an approximate value, the sort that carries a long, possibly non-terminating tail. Catena only reaches for Real when a result genuinely cannot be exact:
- Transcendental functions —
SIN,COS,TAN,LOG,LN,EXP— returnReal. - Roots return
Realonly when the answer is irrational.SQRT(9)is the exactNum3;SQRT(2)is aReal, because its decimal never ends.
The rule of thumb: your prices, totals, and counts stay exact, and only the values that mathematics itself cannot pin down become approximate.
Widening and narrowing
The three numeric types form a ladder — Int, then Num, then Real — from most precise to most general. Catena moves values up the ladder automatically, and only ever moves them down when you ask in writing.
Widening is automatic and silent (Int -> Num -> Real), because it never loses anything you meant to keep. A whole number is already a valid decimal; a decimal is already a valid approximate number. So a function that expects a Real happily accepts a Num, and one that expects a Num accepts an Int. You do not write anything.
Narrowing is never silent. Going the other way — Real -> Num, or Num -> Int — throws away information (a fractional tail, or the digits after the decimal point), so Catena refuses to do it behind your back. You must request it explicitly with AS (below), and the value is rounded according to the sequence's active rounding setting.
SQRT(2) -> diagonal # a Real, 1.41421356...
diagonal AS Num -> rounded # explicit narrowing to an exact decimalThis is the safeguard that keeps a stray approximation from quietly polluting an exact money total.
Optional and enforced typing
Catena uses gradual typing: by default types are inferred and never written, so a sequence reads like ordinary arithmetic. When you want more control, you can opt in at two levels:
-
On a single assignment, add
ASand a type to lock what a value becomes:prompt_value -> age AS Int # this input must be a whole number -
On a whole sequence, turn on strict mode with the setting
ESET STRONGTYPE ON. In strict mode, types must be declared rather than inferred, and any implicit narrowing is rejected. It is the belt-and-braces choice for a sequence you intend to share, where you would rather catch a type mistake up front than discover it in the numbers.
Declared types are checked when the sequence is validated — before it ever runs — so mistakes surface early.
Writing values: the literals
A literal is a value written directly in the source. Catena reads the shape of what you type and gives it the right type automatically.
| Kind | How you write it | Becomes |
|---|---|---|
| Number | 42, 3.14, -5, 1_000_000, 2.5e3 |
Num |
| Money | $399, €20, 1234.56 USD |
a Num amount in that currency |
| Quantity with unit | 5 kg, 30 days, 90 km/h |
a number carrying a unit |
| Date / time | 2026-05-31, 12:30, March 12 2026 |
Date |
| Text | "Net price?" |
Text |
| Boolean | TRUE, FALSE (or YES, NO) |
Bool |
| Bag | [150, 200, 99.50] |
Bag |
A few points worth noting:
- Digit grouping. The underscore in
1_000_000is purely cosmetic — it makes long numbers readable and is ignored by the calculator.1_000_000and1000000are the same value. - Money is a unit.
$399and1234.56 USDare the same idea written two ways: an amount attached to a currency. Because the amount underneath is aNum, currency arithmetic is exact. Units and currencies are covered fully in Units and quantities. - Dates take durations, not bare numbers. To move a date, add a
Duration:today + 5 days. Adding a plain number to a date —today + 5— is an error, because "plus five what?" is genuinely ambiguous; Catena stops and asks rather than guessing.30 daysis aDurationliteral;5 daysadded to aDategives a newDate.
The AS operator: convert or format
AS answers a single question — "give me this value, but as a ___." What fills the blank decides which of two different jobs AS performs:
You write value AS X where X is... |
What happens | Result |
|---|---|---|
| a type (from the type set above) | the value is converted to that type | a value of the new type |
| a format (a rendering name) | the value is rendered as human-readable text | Text |
The distinction matters:
- Converting to a type changes the value.
someDate AS Intturns a date into a whole number you can compute with (a count of seconds, by default).diagonal AS Numnarrows an approximateRealto an exact decimal. - Formatting does not change the value — it produces a
Textrendering, the sort of thing you hand toDISPLAY,PRINT, orOUT. The underlying value is untouched; you are only choosing how it looks.
Catena keeps this unambiguous by making type names and format names two separate sets with no overlap. So AS DateLong can only mean "format as a long date" (there is no type called DateLong), and AS Int can only mean "convert to a whole number."
The built-in formats. In v1 these are bare names that take no parameters:
| Value being rendered | Available formats |
|---|---|
Date |
DateLong, DateShort, DateISO, Time, DateTime |
| number | Money, Percent |
For finer control of how a decimal prints — a fixed number of places, for instance — use the sequence's display-precision and rounding settings (ESET ROUNDING) rather than a format name.
Worked examples
Formatting a date three ways. The same Date value can be displayed in whichever style suits the output. None of these change the date; each produces a piece of Text:
2026-05-31 -> invoiceDate
invoiceDate AS DateLong # "Saturday, 31 May 2026"
invoiceDate AS DateISO # a machine-style date, e.g. 2026-05-31
invoiceDate AS DateShort # a short, locale-aware dateRendering a number as a percentage. A ratio held as a plain number can be shown as a percentage for display, again without altering the stored value:
0.2 -> vatRate
DISPLAY vatRate AS Percent # shown to the reader as a percentageAdding money exactly. Because money amounts are exact Num values, small denominations sum without drift — the classic 0.10 + 0.20 problem simply does not occur:
$0.10 + $0.20 -> total # exactly 0.30, not 0.30000000000004
DISPLAY total AS Money # rendered as currency text for the readerNarrowing an approximate result on purpose. A transcendental result is a Real. If you need it as an exact decimal — to fold it into a money calculation, say — narrow it explicitly, which rounds per the active rounding setting:
LN(10) -> growth # a Real, 2.302585...
growth AS Num -> rounded # explicit, deliberate narrowingConverting a date to a number. When you genuinely need arithmetic on a date — the gap between two instants as a raw count, for example — convert it to an Int. This is allowed precisely because you asked for it in writing; only silent date-to-number coercion is dangerous, and Catena never does that:
invoiceDate AS Int -> stamp # a whole-number time stampThe pattern across all of these is the same: let Catena infer types while you type, reach for AS when you want to convert a value or dress it up for display, and trust that money and counts stay exact unless you deliberately step off that path.
See also
- Language basics — lines, names, and the
->assignment used throughout this chapter. - Expressions and operators — how values combine, and where
NumversusRealcomes into play. - Units and quantities — money as a unit, quantities like
5 kgand90 km/h, and unit conversion.