Chapters

Engine settings with ESET

Two calculators can agree on every formula and still disagree on the answer. One rounds half-up, the other rounds to even; one counts a date from 1970, the other from 1900; one enforces declared types, the other guesses. When you write a sequence and hand it to someone else, none of that can be left to their app's preferences — the sequence has to carry its own rules with it. ESET is how a sequence does that. It writes a handful of engine behaviours directly into the program, so the sequence computes the same way on every machine that runs it.

If you are new to Catena you can skip this chapter entirely for now: every setting has a sensible default, and a sequence with no ESET line at all runs correctly. Come back when you need a specific rounding rule, a bigger loop budget, or stricter typing.


What ESET does

ESET stands for Engine SETting. It fixes one piece of engine behaviour for the sequence it appears in. Rounding mode, how a date converts to an integer, whether types are enforced, how many past answers are remembered, how large a loop or how deep a recursion is allowed — these are not part of a formula, but they change what a formula produces. ESET pins them to the sequence so the result does not depend on whoever happens to run it.

The reason to put these in the language rather than in app-wide options is portability. An accounting firm that writes a sequence for its staff needs to guarantee how it computes, not hope that every workstation is configured identically. A sequence that begins with ESET ROUNDING BANK rounds to banker's rounding wherever it runs, regardless of the local app settings.


The form

Every engine setting is written on its own line, in the same shape:

ESET <SETTING> <VALUE>

The keyword ESET, then the setting name, then the value. Both the keyword and the setting name are case-insensitive, so ESET ROUNDING BANK and eset rounding bank are the same statement. A few examples:

ESET ROUNDING BANK        # use banker's rounding
ESET STRONGTYPE ON        # enforce declared types
ESET MAXLOOP 100000       # allow up to 100000 loop iterations

If you name a setting the engine does not recognise, the sequence is rejected before it runs, with a message suggesting the setting you probably meant. You cannot misspell a setting and have it silently ignored.


The settings

The settings below are the complete set Catena recognises. For each, the first listed value is the default — the behaviour you get with no ESET line at all.

Setting What it controls Values (default first)
ROUNDING The rounding mode used when a number is narrowed or when money is rounded. HALFUP, BANK, DOWN, UP, HALFEVEN
DATEINT How a date becomes an integer when you convert Date AS Int. UNIX (count from 1970), WINDOWS (the spreadsheet-style serial), NONE (forbid the conversion)
STRONGTYPE Whether declared types are enforced. OFF infers types and allows safe widening; ON requires types to be declared and rejects implicit narrowing. OFF, ON
OFFLINE Whether the sequence attempts external API calls. OFF always tries; AUTO skips the call and asks you instead when there is no connectivity; ON never tries and always asks. OFF, AUTO, ON
ANSDEPTH How many recent answers the Ans history keeps. engine default, then any whole number ≥ 1
MAXLOOP The maximum number of iterations any one loop may run before the engine stops it. engine default, then any whole number
MAXDEPTH The maximum recursion depth — how many nested sequence calls are allowed before the engine stops. engine default, then any whole number

MAXLOOP and MAXDEPTH are safety limits, not performance tuning. Catena will not let a sequence hang: if a loop runs past MAXLOOP or a recursion nests past MAXDEPTH, the engine stops with a clear message rather than freezing. Raising the limit is how you tell the engine that a genuinely large computation is intended and not a runaway.

Catena also recognises a LOCALE setting, which selects the localization applied within the sequence's scope. Keywords are always canonical English underneath; localization is a presentation layer over them, so LOCALE changes how the sequence is shown and read rather than what it computes. The spec does not enumerate the accepted LOCALE values, so treat it as naming a language or locale and confirm the exact form against your app's language list before relying on it. See Types and formatting for how formats and locale interact with display.


Scope and inheritance

An ESET is not global. It applies to the sequence it appears in and to every sequence called from it — it inherits downward. This is the one thing that flows across a CALL: variables and rules stay local to each sequence, but engine settings are ambient configuration and travel down into callees.

That gives you a clean way to set policy once. If a top-level sequence sets ESET ROUNDING BANK and then calls three helper sequences, all three round the same way without repeating the line. The app-wide options provide the base values; an ESET overrides them for its scope and everything beneath it.

A callee can still override a setting for its own work. When it does, the override applies only while that callee runs; as soon as control returns to the caller, the setting reverts to what the caller had. Think of it as a stack that unwinds on return: an inner sequence can tighten or relax a setting locally without disturbing the sequence that called it.

For how CALL and RETURN move control between sequences, see Sequences and functions.


Worked examples

Setting a rounding rule. A billing sequence must round every total to banker's rounding (round-half-to-even), which is common in accounting because it does not bias repeated roundings upward. Put the setting at the top so it governs the whole sequence and anything it calls:

ESET ROUNDING BANK

subtotal -> 12.125
tax      -> subtotal × 0.20
total    -> subtotal + tax AS Money

Every narrowing and every Money rounding below the ESET line now uses banker's rounding. Move the sequence to another machine and it still does.

Raising a loop limit. A sequence sums a long series and needs more iterations than the default budget allows. Raise MAXLOOP before the loop so the engine permits the larger run instead of stopping it as a suspected runaway:

ESET MAXLOOP 500000

running -> 0
REPEAT WITH n FROM 1 TO 400000
    running -> running + (1 / (n × n))
END

Without the raised limit, a loop of 400000 iterations could trip the default cap and stop with a bounded-execution message. The ESET line states that this size is intended.

Enforcing strong typing. In a sequence where a wrong-type value would be a real mistake, turn on enforced typing so the validator rejects implicit narrowing and requires declarations, rather than inferring types and quietly widening:

ESET STRONGTYPE ON

count AS Int   -> 40
rate  AS Num   -> 0.075
owed  AS Money -> count × rate

With STRONGTYPE ON, each value must carry its declared type and the engine refuses a silent narrowing (for example, assigning a fractional Num into an Int). The sequence is checked against these declarations before it runs, so type mistakes surface as validation errors rather than surprises at run time. More on the type catalog and declarations is in Types and formatting.