refactor!: retire Index, intern and lookup
`Index` was public, `History::intern` and `History::lookup` returned
one, and no public method anywhere accepted one. It was a handle with
nowhere to go — and `key_table.rs` advertised the hot-path story it was
meant to enable ("power users can promote `&K` to `Index` and skip the
lookup"), which was never reachable through the public API.
It also shadowed `std::ops::Index`, which `CompetitorStore` implements,
so `use trueskill_tt::*` alongside `use std::ops::*` collided.
All three are `pub(crate)` now. `intern` stays internal because
ingestion needs it; `lookup` is gone entirely, since `current_skill`,
`rating` and `learning_curve` already answer "does this history know
this key" and all three take a borrowed key.
The three tests that used them asserted things a caller cannot observe.
They now assert what the interning bought:
- `record_winner_creates_two_competitors` compares posteriors instead of
comparing two opaque indices for inequality.
- `intern_is_idempotent` becomes `a_repeated_key_is_one_competitor` — a
key appearing in two events gives one competitor with a two-point
learning curve, which is the observable form of the same claim.
- `lookup_returns_none_for_missing` becomes `an_unknown_key_is_unknown`.
Closes #73.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hcFjNDmHXZF8URGLku5zZ
This commit is contained in:
+9
-22
@@ -538,29 +538,16 @@ impl<T: Time, K: Eq + Hash + Clone> HistoryBuilder<T, ConstantDrift, NullObserve
|
||||
impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O, K> {
|
||||
/// Promote a key to its [`Index`], creating the entry if it is new.
|
||||
///
|
||||
/// Interning a key does not register a competitor or give them a rating —
|
||||
/// it only reserves the slot. Use [`History::register`] to declare a
|
||||
/// competitor's configuration up front.
|
||||
pub fn intern<Q>(&mut self, key: &Q) -> Index
|
||||
/// Crate-internal since #73: interning reserves a storage slot and nothing
|
||||
/// more, and no public method ever accepted the handle it returned.
|
||||
/// [`History::register`] is the public way to declare a competitor.
|
||||
pub(crate) fn intern<Q>(&mut self, key: &Q) -> Index
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq + ToOwned<Owned = K> + ?Sized,
|
||||
{
|
||||
self.keys.get_or_create(key)
|
||||
}
|
||||
|
||||
/// Resolve an existing key to its [`Index`], or `None` if the history has
|
||||
/// never seen it.
|
||||
///
|
||||
/// The read-only counterpart of [`History::intern`]: it never creates.
|
||||
#[must_use]
|
||||
pub fn lookup<Q>(&self, key: &Q) -> Option<Index>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
self.keys.get(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O, K> {
|
||||
@@ -1328,7 +1315,7 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
|
||||
/// Every key must already be known to the history — that is, must have
|
||||
/// appeared in an ingested event. An unknown key is `UnknownKey`, not a
|
||||
/// silently dropped member. If your caller cannot guarantee that, pre-filter
|
||||
/// with [`History::lookup`] or [`History::current_skill`]; treating the
|
||||
/// with [`History::current_skill`] or [`History::competitors`]; treating the
|
||||
/// error as "no information" and substituting a neutral value turns a
|
||||
/// whole-team miss into a plausible constant, which is invisible to any
|
||||
/// test that does not assert on variation.
|
||||
@@ -1849,7 +1836,7 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
|
||||
/// Every key must already be known to the history — that is, must have
|
||||
/// appeared in an ingested event. An unknown key is `UnknownKey`, not a
|
||||
/// silently dropped member. If your caller cannot guarantee that, pre-filter
|
||||
/// with [`History::lookup`] or [`History::current_skill`]; treating the
|
||||
/// with [`History::current_skill`] or [`History::competitors`]; treating the
|
||||
/// error as "no information" and substituting a neutral value turns a
|
||||
/// whole-team miss into a plausible constant, which is invisible to any
|
||||
/// test that does not assert on variation.
|
||||
@@ -1913,7 +1900,7 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
|
||||
/// Every key must already be known to the history — that is, must have
|
||||
/// appeared in an ingested event. An unknown key is `UnknownKey`, not a
|
||||
/// silently dropped member. If your caller cannot guarantee that, pre-filter
|
||||
/// with [`History::lookup`] or [`History::current_skill`]; treating the
|
||||
/// with [`History::current_skill`] or [`History::competitors`]; treating the
|
||||
/// error as "no information" and substituting a neutral value turns a
|
||||
/// whole-team miss into a plausible constant, which is invisible to any
|
||||
/// test that does not assert on variation.
|
||||
@@ -1966,7 +1953,7 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
|
||||
/// Every key must already be known to the history — that is, must have
|
||||
/// appeared in an ingested event. An unknown key is `UnknownKey`, not a
|
||||
/// silently dropped member. If your caller cannot guarantee that, pre-filter
|
||||
/// with [`History::lookup`] or [`History::current_skill`]; treating the
|
||||
/// with [`History::current_skill`] or [`History::competitors`]; treating the
|
||||
/// error as "no information" and substituting a neutral value turns a
|
||||
/// whole-team miss into a plausible constant, which is invisible to any
|
||||
/// test that does not assert on variation.
|
||||
@@ -2016,7 +2003,7 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
|
||||
/// Every key must already be known to the history — that is, must have
|
||||
/// appeared in an ingested event. An unknown key is `UnknownKey`, not a
|
||||
/// silently dropped member. If your caller cannot guarantee that, pre-filter
|
||||
/// with [`History::lookup`] or [`History::current_skill`]; treating the
|
||||
/// with [`History::current_skill`] or [`History::competitors`]; treating the
|
||||
/// error as "no information" and substituting a neutral value turns a
|
||||
/// whole-team miss into a plausible constant, which is invisible to any
|
||||
/// test that does not assert on variation.
|
||||
|
||||
Reference in New Issue
Block a user