Skip to content

Accept Into<f64> for numeric API boundaries #997

Description

@bbbruce

Summary

I would like to discuss whether some ixa APIs that currently accept raw f64 values should instead accept T: Into<f64> / impl Into<f64>.

This would preserve existing f64 call sites while improving ergonomics for users who wrap numeric values in lightweight domain types, such as:

  • OrderedFloat<f64> for sortable/hashable model times
  • validated probability newtypes that guarantee values are in [0.0, 1.0]

I have not done a full scan of the public API yet, so this issue is mainly to start discussion before digging deeper.

Motivation

Some downstream model code uses typed wrappers around f64 values for scientific/modeling safety and domain clarity.

Two examples that seem relevant to ixa's public interface:

  1. Context::add_plan

Currently this takes a raw f64 time. If a model stores event times as OrderedFloat<f64>, callers need to write:

context.add_plan(time.into_inner(), callback);

Since OrderedFloat<f64> already implements conversion into f64, an Into<f64> boundary could allow:

context.add_plan(time, callback);
  1. ContextRandomExt::sample_bool

Currently this takes a raw f64 probability. If a model uses a validated probability type, callers need to write:

context.sample_bool(MyRng, probability.into());

With an Into<f64> boundary, this could become:

context.sample_bool(MyRng, probability);

Possible API Shape

For example:

    fn sample_bool<R, P>(&self, rng_id: R, p: P) -> bool
    where
        R: RngId + 'static,
        R::RngType: Rng,
        P: Into<f64>,
    {
        let p = p.into();
        self.sample(rng_id, |rng| rng.random_bool(p))
    }

And similarly:

    fn add_plan<T>(
        &mut self,
        time: T,
        callback: impl FnOnce(&mut Context) + 'static,
    ) -> PlanId
    where
        T: Into<f64>,
    {
        let time = time.into();
        ...
    }

Exact signatures may need adjustment depending on existing trait structure.

Why This Seems Helpful

This should be backward compatible for existing f64 callers, while making it easier for users to adopt safer domain-specific numeric types.

Potential benefits:

  • Better ergonomics for typed model code
  • Less repetitive .into() / .into_inner() at ixa API boundaries
  • Encourages scientifically safer modeling patterns, such as validated probabilities
  • Keeps validation/domain typing in user code without forcing ixa to own those domain types
  • Should be zero runtime overhead after monomorphization

Caveats

This does not make ixa APIs inherently validate probability or time semantics.

For example, sample_bool(p) would still ultimately rely on rand::random_bool, which checks probability range at runtime and panics for invalid values. Accepting Into<f64> simply lets users pass already-validated probability wrappers directly.

Similarly, accepting Into<f64> for add_plan would not by itself define a stronger model-time type. It would only make wrapper types easier to use at the boundary.

Questions for Discussion

  • Do we want this kind of generic numeric boundary in ixa APIs?
  • Are sample_bool and add_plan good candidates?
  • Are there other public APIs where this pattern would be useful?
  • Is Into<f64> the right trait, or would a more explicit trait be preferable?
  • Are there any downsides for type inference, docs, or API clarity?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions