// 979: Future/Promise Basics
// Rust async fn + await โ showing the monad connection in pure std code
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
// --- A minimal synchronous executor (no tokio needed) ---
fn block_on<F: Future>(mut fut: F) -> F::Output {
// Safety: we pin the future on the stack
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
// Create a no-op waker
fn noop(_: *const ()) {}
fn noop_clone(p: *const ()) -> RawWaker { RawWaker::new(p, &VTABLE) }
static VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
let raw = RawWaker::new(std::ptr::null(), &VTABLE);
let waker = unsafe { Waker::from_raw(raw) };
let mut cx = Context::from_waker(&waker);
// For simple futures that resolve immediately, one poll is enough
match fut.as_mut().poll(&mut cx) {
Poll::Ready(v) => v,
Poll::Pending => panic!("Future not ready โ use a real executor for async I/O"),
}
}
// --- Approach 1: async fn is syntactic sugar for impl Future ---
async fn compute_value() -> i32 {
42
}
async fn compute_and_add() -> i32 {
let x = compute_value().await; // bind: unwrap the future
x + 1
}
async fn double_result() -> i32 {
let x = compute_and_add().await;
x * 2 // map: transform the value
}
// --- Approach 2: async block as lambda ---
async fn pipeline(input: i32) -> i32 {
// Sequential monadic chain via .await
let step1 = async { input * 2 }.await;
let step2 = async { step1 + 10 }.await;
let step3 = async { step2.to_string().len() as i32 }.await;
step3
}
// --- Approach 3: Manual Future implementing the trait ---
struct ImmediateFuture<T>(Option<T>);
impl<T: Unpin> Future for ImmediateFuture<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
Poll::Ready(self.0.take().expect("polled after completion"))
}
}
fn immediate<T>(val: T) -> ImmediateFuture<T> {
ImmediateFuture(Some(val))
}
async fn use_manual_future() -> i32 {
immediate(100).await + immediate(23).await
}
fn main() {
let result = block_on(double_result());
println!("double_result: {}", result);
let result2 = block_on(pipeline(5));
println!("pipeline(5): {}", result2);
let result3 = block_on(use_manual_future());
println!("manual future: {}", result3);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_value() {
assert_eq!(block_on(compute_value()), 42);
}
#[test]
fn test_compute_and_add() {
assert_eq!(block_on(compute_and_add()), 43);
}
#[test]
fn test_double_result() {
assert_eq!(block_on(double_result()), 86);
}
#[test]
fn test_pipeline() {
// 5*2=10, 10+10=20, len("20")=2
assert_eq!(block_on(pipeline(5)), 2);
}
#[test]
fn test_manual_future() {
assert_eq!(block_on(use_manual_future()), 123);
}
#[test]
fn test_async_is_lazy() {
// Creating a future does NOT run it โ laziness like OCaml's thunk
let _fut = compute_value(); // nothing runs here
let result = block_on(_fut);
assert_eq!(result, 42);
}
}
(* 979: Future/Promise Basics *)
(* OCaml: Lwt monad concept shown with pure Option/Result monads *)
(* We model the Future/Promise monad pattern without external deps *)
(* --- Approach 1: Model Future as a lazy thunk (pure simulation) --- *)
type 'a future = unit -> 'a
let return_ x : 'a future = fun () -> x
let bind (f : 'a future) (k : 'a -> 'b future) : 'b future =
fun () -> k (f ()) ()
let map (f : 'a -> 'b) (fut : 'a future) : 'b future =
fun () -> f (fut ())
let run fut = fut ()
let () =
let fut = return_ 42 in
let fut2 = bind fut (fun x -> return_ (x + 1)) in
let fut3 = map (fun x -> x * 2) fut2 in
assert (run fut3 = 86);
Printf.printf "Approach 1 (lazy thunk future): %d\n" (run fut3)
(* --- Approach 2: Future as Result monad (error-aware) --- *)
type ('a, 'e) result_future = unit -> ('a, 'e) result
let ok x : ('a, 'e) result_future = fun () -> Ok x
let err e : ('a, 'e) result_future = fun () -> Error e
let bind_r (f : ('a, 'e) result_future) (k : 'a -> ('b, 'e) result_future) : ('b, 'e) result_future =
fun () -> match f () with
| Ok v -> k v ()
| Error e -> Error e
let () =
let computation =
bind_r (ok 10) (fun x ->
bind_r (ok 20) (fun y ->
ok (x + y)))
in
(match computation () with
| Ok v -> assert (v = 30); Printf.printf "Approach 2 (result future): %d\n" v
| Error _ -> assert false)
(* --- Approach 3: Promise with state (mutable cell) --- *)
type 'a promise_state = Pending | Resolved of 'a
type 'a promise = { mutable state : 'a promise_state }
let make_promise () = { state = Pending }
let resolve p v = p.state <- Resolved v
let await p =
match p.state with
| Resolved v -> v
| Pending -> failwith "promise not yet resolved"
let () =
let p = make_promise () in
resolve p 99;
let v = await p in
assert (v = 99);
Printf.printf "Approach 3 (promise state): %d\n" v
let () = Printf.printf "โ All tests passed\n"