Skip to main content

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/reference/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 hydro_lang::live_collections::stream::ExactlyOnce;
58///
59/// use std::fmt::Debug;
60/// use std::time::Duration;
61///
62/// /// ...
63/// ///
64/// /// # Non-Determinism
65/// /// - `nondet_samples`: this function will non-deterministically print elements
66/// ///   from the stream according to a timer
67/// fn print_samples<T: Debug, L>(
68///   stream: Stream<T, Process<L>, Unbounded>,
69///   nondet_samples: NonDet
70/// ) {
71///   stream
72///     .sample_every(q!(Duration::from_secs(1)), nondet!(
73///       /// non-deterministic timing will result in non-determistic samples printed
74///       nondet_samples
75///     ))
76///     .assume_retries::<ExactlyOnce>(nondet!(
77///         /// non-deterministic duplicated logs are okay
78///         nondet_samples
79///     ))
80///     .for_each(q!(|v| println!("Sample: {:?}", v)))
81/// }
82/// ```
83macro_rules! __nondet__ {
84    ($(#[doc = $doc:expr])+$($forward:ident),*) => {
85        {
86            $(let _ = $forward;)*
87            $crate::nondet::NonDet
88        }
89    };
90}