hydro_lang/
nondet.rs

1//! Defines the `NonDet` type and `nondet!` macro for tracking non-determinism.
2//!
3//! All **safe** APIs in Hydro guarantee determinism, even in the face of networking delays
4//! and concurrency across machines. But often it is necessary to do something non-deterministic,
5//! like generate events at a fixed wall-clock-time interval, or split an input into arbitrarily
6//! sized batches.
7//!
8//! These non-deterministic APIs take additional parameters called **non-determinism guards**.
9//! These values, with type `NonDet`, help you reason about how non-determinism affects your
10//! application. To pass a non-determinism guard, you must invoke `nondet!()` with an explanation
11//! for how the non-determinism affects the application.
12//!
13//! See the [Hydro docs](https://hydro.run/docs/hydro/live-collections/determinism) for more.
14
15/// A non-determinism guard, which documents how a source of non-determinism affects the application.
16///
17/// To create a non-determinism guard, use the [`nondet!`] macro, which takes in a doc comment
18/// explaining the effects of the particular source of non-determinism, and additional
19/// non-determinism guards that justify the form of non-determinism.
20#[derive(Copy, Clone)]
21pub struct NonDet;
22
23#[doc(inline)]
24pub use crate::__nondet__ as nondet;
25
26#[macro_export]
27/// Fulfills a non-determinism guard parameter by declaring a reason why the
28/// non-determinism is tolerated or providing other non-determinism guards
29/// that forward the inner non-determinism.
30///
31/// The first argument must be a doc comment with the reason the non-determinism
32/// is okay. If forwarding a parent non-determinism, because the non-determinism
33/// is not handled internally, you should provide a short explanation of how the
34/// inner non-determinism is captured by the outer one. If the non-determinism
35/// is locally resolved, you should document _why_ this is the case.
36///
37/// # Examples
38/// Locally resolved non-determinism:
39/// ```rust,no_run
40/// # use hydro_lang::prelude::*;
41/// use std::time::Duration;
42///
43/// fn singleton_with_delay<T, L>(
44///   singleton: Singleton<T, Process<L>, Unbounded>
45/// ) -> Optional<T, Process<L>, Unbounded> {
46///   singleton
47///     .sample_every(q!(Duration::from_secs(1)), nondet!(/**
48///         non-deterministic samples will eventually resolve to stable result
49///     */))
50///     .last()
51/// }
52/// ```
53///
54/// Forwarded non-determinism:
55/// ```rust
56/// # use hydro_lang::prelude::*;
57/// use std::fmt::Debug;
58/// use std::time::Duration;
59///
60/// /// ...
61/// ///
62/// /// # Non-Determinism
63/// /// - `nondet_samples`: this function will non-deterministically print elements
64/// ///   from the stream according to a timer
65/// fn print_samples<T: Debug, L>(
66///   stream: Stream<T, Process<L>, Unbounded>,
67///   nondet_samples: NonDet
68/// ) {
69///   stream
70///     .sample_every(q!(Duration::from_secs(1)), nondet!(
71///       /// non-deterministic timing will result in non-determistic samples printed
72///       nondet_samples
73///     ))
74///     .assume_retries(nondet!(
75///         /// non-deterministic duplicated logs are okay
76///         nondet_samples
77///     ))
78///     .for_each(q!(|v| println!("Sample: {:?}", v)))
79/// }
80/// ```
81macro_rules! __nondet__ {
82    ($(#[doc = $doc:expr])+$($forward:ident),*) => {
83        {
84            $(let _ = $forward;)*
85            $crate::nondet::NonDet
86        }
87    };
88}