Frequency model¶
Work in progress
This page is a stub. Paper-voice content arrives in a follow-up writing pass.
Three design choices that diverge from the Julia upstream:
-
Cached-singleton frozen dataclasses.
Yearly()returns the same object asYearly()— equality is identity. Frequencies are slotted dataclasses with a tiny per-class instance cache so the runtime cost of "compare two frequencies" is oneischeck. -
Constructor functions, not literal sugar. Julia writes
2020Q1; the Python analogue isqq(2020, 1). We don't overloadint * Q1because there is no precedent in the scientific Python stack for*returning anything but a number, and discoverability matters more than parity with the Julia spelling. -
MITis not equal to a bareint. Julia'sMIT{Quarterly}interconverts withIntfreely. Python can't do that without breaking the__eq__/__hash__invariant required bydictandset, soqq(2020, 1) == 8080isFalse. Useint(mit)to extract the underlying offset explicitly.
Why cached singletons?¶
Yearly() is conceptually a type, not a value. The Julia upstream encodes that
with Yearly{end_month} as a parametric type. Python doesn't have first-class
parametric types at runtime, so we approximate them with frozen dataclasses, and
we cache the instances because the alternative ("construct a fresh Yearly()
each call site") would invalidate every is-based hot path.
Why no 2020Q1 sugar?¶
A reader's first encounter with 2020Q1 in Julia produces a MIT{Quarterly}
literal. The equivalent in Python — __rmul__ on a Q1 sentinel object,
returning MIT(Quarterly(), …) — would be cute but would also be the only
place in the scientific Python ecosystem where * returns a non-number. We
chose the readable spelling (qq(2020, 1)) over the cute one.