summaryrefslogtreecommitdiff
path: root/lib/kernel/rite.mli
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/rite.mli')
-rw-r--r--lib/kernel/rite.mli62
1 files changed, 62 insertions, 0 deletions
diff --git a/lib/kernel/rite.mli b/lib/kernel/rite.mli
new file mode 100644
index 0000000..ffe9471
--- /dev/null
+++ b/lib/kernel/rite.mli
@@ -0,0 +1,62 @@
+(** Everything a rite supplies, bundled. Passing these as loose arguments let a
+ caller pair one rite's vocab with another's temporal; bundling makes that
+ unrepresentable through the normal path. Carries functions, so it has no
+ sexp form. *)
+type ('s, 'r) t = {
+ id : string;
+ vocab : ('s, 'r) Vocab.t;
+ year_start : int -> Date.t;
+ (** first day of the liturgical year opening in civil year y *)
+ temporal : Date.t -> ('s, 'r) Temporal.t;
+ anchors : int -> (string * Date.t) list;
+ (** Easter-derived days: (expected slug, date) *)
+ rules : ('s, 'r) Precedence.rules;
+ season_runs : 's list;
+ (** the expected run-length-compressed season sequence over one liturgical
+ year. NOT necessarily [vocab.seasons]: a rite may have one season
+ appear in two separate runs (the modern form's Ordinary Time does). *)
+ transfer_target :
+ 'r Precedence.candidate -> Date.t -> (Date.t -> 'r Celebration.t) -> Date.t;
+ (** RG 96: where an impeded I-class feast goes. Given the deferred
+ candidate, the date it was impeded on, and [occupant] -- a callback
+ exposing what {!Calendar} currently resolves as observed on any
+ given date -- returns the date to place it on.
+
+ Deliberately one rite-supplied function, not a generic search Calendar
+ drives itself: "not I or II class" is not derivable from [band] or
+ [disposition] alone. RG 91's own table would let a universal I-class
+ feast (entry 11) numerically outrank an ordinary Sunday (entry 15,
+ II class) in a raw occurrence contest -- entry 11 comes before entry
+ 15, and lower wins -- so testing "would the translated feast win
+ here" is not the same question as "is this day free to receive a
+ translation": RG 96 forbids landing on the Sunday regardless of
+ which one would structurally win. Only the rite knows which of its
+ own ranks are exempt from translation onto them. The rite also
+ owns the search's starting point, because RG 96's exception is
+ rite-specific too: the Annunciation does not search forward from
+ its own impeded date at all, it goes straight to the Monday after
+ Low Sunday (searching onward from there only if that day is itself
+ blocked). [occupant] is supplied rather than a raw layer/temporal
+ pair so the rite never has to re-implement occurrence resolution
+ just to answer "what sits here".
+
+ OBLIGATIONS (not enforced by the type, and {!Calendar}'s own
+ termination argument depends on both): the result must be
+ {b strictly later} than the [Date.t] argument (the date the
+ candidate was impeded on) -- {!Calendar}'s placement pass treats
+ [target = origin] or [target < origin] as a legitimate placement,
+ not an error, so a rite whose search can stand still or go
+ backward would silently loop candidates in place or resurrect an
+ already-superseded occupant rather than failing loudly. The call
+ must also {b terminate} on its own: {!Calendar}'s round guard
+ (calendar.ml's [max_transfer_rounds]) bounds how many ROUNDS the
+ whole-year placement pass takes, which is a distinct, outer thing
+ from whatever internal search a single call to this function runs
+ -- an implementation that walks forward day by day looking for an
+ admissible date, without its own bound, can hang the caller
+ outright on a rite/data shape it does not handle, never reaching
+ the round guard at all. See rite_ef/precedence_ef.ml's
+ [transfer_target] for a concrete termination argument (a
+ structural step bound, not an appeal to the real calendar's own
+ structure). *)
+}