Skip to content

payload config: add new Probability<const u32: MIN_AS_BITS> type#1874

Merged
goxberry merged 12 commits into
mainfrom
goxberry/create-probability-type
May 19, 2026
Merged

payload config: add new Probability<const u32: MIN_AS_BITS> type#1874
goxberry merged 12 commits into
mainfrom
goxberry/create-probability-type

Conversation

@goxberry
Copy link
Copy Markdown
Contributor

@goxberry goxberry commented May 12, 2026

What does this PR do?

Adds a new Probability<const u32: MIN_AS_BITS> type as a proof-of-concept for configuration fields that accept f32 values interpreted as probabilities. The type isn't used yet. The MIN_AS_BITS field is intended for use as a lower bound in cases where the minimum probability for a configuration field should be positive, e.g., unique tag probability of at least 0.1.

Motivation

Reduce some boilerplate validation code related to probability-valued configuration fields for payloads.

Related issues

n/a

Additional Notes

Tests and ergonomics are still a work in progress. The plan files will be removed prior to merging the PR. These files are present in case reviewers find them helpful, but I don't intend them to clutter the repo.

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@goxberry goxberry marked this pull request as ready for review May 12, 2026 20:36
@goxberry goxberry requested a review from a team as a code owner May 12, 2026 20:36
@goxberry goxberry changed the title docs: add plan for f32 probability type payload config: add new Probability<const u32: MIN_AS_BITS> type May 12, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 979097bcb4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lading_payload/src/common/config.rs Outdated
/// Error returned when a value cannot be turned into a [`Probability`].
#[derive(Debug, thiserror::Error, Clone, Copy)]
pub enum ProbabilityError {
/// Value is NaN or `±∞`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Replace non-ASCII probability docs

/workspace/lading/AGENTS.md requires "US-ASCII only - no Unicode characters in code or documentation", but this addition introduces non-ASCII glyphs here and in the new plan docs (for example ±∞, , em dashes, and ). Since the rule applies to both Rust documentation and Markdown, replace these with ASCII equivalents such as +/-inf, <->, --, and in before merging.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 1a6683d.

Comment on lines +96 to +100
Approximately 18 unit tests collapse into 12 generic property tests
(of which the `MIN`-parameterized ones expand to three `#[test]` functions
each) plus 6 retained unit tests. Input-space coverage broadens substantially
while the literal wire formats and discrete edge cases (`-0.0`, `[1.0, 1.0]`)
remain pinned by unit tests.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat.

@datadog-official

This comment has been minimized.

goxberry and others added 12 commits May 19, 2026 13:29
Introduces an f32-valued probability type in lading_payload::common::config
whose lower bound is carried as a const-generic u32 (an IEEE-754 bit pattern
obtained at the call site via f32::to_bits). The decoded bound is validated
at compile time via assertions in `Self::MIN` against the constraints in
docs/plans/probability-type.md: finite, in [+0.0, +1.0], not -0.0.

Runtime construction goes through `try_new`, which rejects NaN, ±∞, -0.0,
values below `MIN`, and values above 1.0 with a typed `ProbabilityError`.
`Self::MIN` is referenced inside the constructor so that the const-eval
checks fire for every monomorphization.

Serde wiring and a Display impl follow in subsequent commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires Probability<MIN_BITS> into Serde with the canonical newtype-validation
pattern: `#[serde(into = "f32", try_from = "f32")]` together with
`TryFrom<f32>` (delegating to `try_new`) and `From<Probability<_>> for f32`.

On the wire the type is a bare f32, so existing YAML/JSON configuration that
currently uses an unvalidated f32 field can be migrated to this type
without breaking deserialization. Invalid values are rejected at parse time
with the same ProbabilityError variants surfaced through serde's error path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delegates to the inner f32's Display so format specifiers like `{:.3}`
propagate. Mirrors the existing ConfRange<T>: Display impl in the same
module, which similarly does not surface its type parameter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document why MIN_BITS is a u32 (stable Rust lacks f32 const generics) and
the IEEE-754 properties of the f32 <-> u32 round trip that make integer-
domain bound checks viable on non-negative finite values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename MIN_BITS to MIN_AS_BITS and NEG_ZERO_BITS to NEG_ZERO_AS_BITS to
make the f32-encoded-as-u32 representation explicit at every use site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace per-case unit tests for `Probability<MIN_AS_BITS>` with proptest
properties covering value validation, Display, and serde (JSON + YAML)
round-trips and rejections. Each MIN-parameterized property is realized
as a small generic helper invoked from one `proptest!` block per type
alias (ZeroOrMore, AtLeastHalf, AtLeastOne).

Kept as unit tests where exact wire format or a discrete edge case
matters: the const decoding pins, the `[1.0, 1.0]` unit-bound case, the
`-0.0` rejection, and the JSON/YAML wire-format pins for `0.75`.

`valid_value_strategy` is boxed and branches on `min >= 1.0` because
proptest's `RangeInclusive<f32>` strategy panics on degenerate ranges.

The `non_finite_strategy` uses only `Just(NAN)`, `Just(INFINITY)`, and
`Just(NEG_INFINITY)` rather than additionally filtering `any::<f32>()`
as the plan text suggests — the filter would exhaust proptest's
rejection budget, and the three discrete values already exercise every
branch of the `is_finite()` rejection path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove the `ignore` fence on the Probability example so it compiles and
runs as part of `cargo test --doc`. Since 0.75 is exactly representable
in f32, tighten the assertion to an equality check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the explicit `-0.0` rejection with normalization to `+0.0` so
configs containing `-0.0` no longer fail validation. The order-isomorphism
between `f32` and `u32` bit patterns that the type relies on is preserved
because stored values are never `-0.0`. Drop the `ProbabilityError::NegativeZero`
variant.

BREAKING CHANGE: `ProbabilityError::NegativeZero` is removed. Callers
matching on it should drop the arm; `-0.0` now succeeds (for `MIN <= 0.0`)
or surfaces as `BelowMin` with `value: +0.0` (for `MIN > 0.0`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All operations in `try_new` (Self::MIN, f32::is_finite, f32::to_bits, f32
comparisons, enum/struct construction) are const-stable on the pinned
Rust 1.90 toolchain, so the function can be marked `const`. This lets
callers validate `Probability` literals at compile time by matching on
the returned `Result`.

Add `try_new_is_const_evaluable` to lock in the property: it materializes
both a successful and a failing `try_new` call into `const` bindings, so
losing `const` would break the build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The type doc previously hinted that bound checks might one day migrate
from the f32 domain to u32 comparisons on `value.to_bits()`. In practice
that change would add branches (to handle negative inputs without
mis-classifying them as `AboveOne`) and saves no measurable time on a
non-hot path, so the promise was misleading.

Recast the IEEE-754 properties paragraph so the bullets stand on their
own as design rationale for the `-0.0` normalization and the bit-pattern
`Hash` impl, rather than as setup for a future integer-domain
comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `±∞`, `+∞`, and `↔` with ASCII equivalents (`<->`, `+inf`) and
intra-doc links to `f32::NAN`, `f32::INFINITY`, and `f32::NEG_INFINITY`
for portability and better rustdoc cross-referencing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Explain that the const generic parameter exists to support fields with a
strict positive lower bound, citing `unique_tag_probability` in
`common::tags::Generator` as the motivating use case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@goxberry goxberry force-pushed the goxberry/create-probability-type branch from c63855a to febc1e4 Compare May 19, 2026 20:29
@goxberry
Copy link
Copy Markdown
Contributor Author

Will submit a follow-on PR with a few more ergonomics tweaks, and wire up payload configs to see whether this experiment proves out.

@goxberry goxberry merged commit a2ed1be into main May 19, 2026
32 checks passed
@goxberry goxberry deleted the goxberry/create-probability-type branch May 19, 2026 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants