Skip to main content

a_do

Macro a_do 

Source
a_do!() { /* proc-macro */ }
Expand description

Applicative do-notation.

Desugars flat applicative syntax into pure / map / lift2-lift5 calls, matching PureScript ado notation. Unlike m_do!, bindings are independent: later bind expressions cannot reference earlier bound variables. Supports both explicit-brand and inferred-brand modes.

§Syntax

// Explicit mode
a_do!(Brand {
    x <- expr;            // Bind: independent applicative computation
    y: Type <- expr;      // Typed bind: with explicit type annotation
    _ <- expr;            // Discard bind: compute for effect
    expr;                 // Sequence: shorthand for `_ <- expr;`
    let z = expr;         // Let binding: placed inside the combining closure
    expr                  // Final expression: the combining body
})

// Inferred mode (brand inferred from container types)
a_do!({
    x <- Some(3);
    y <- Some(4);
    x + y
})

// By-reference modes:
a_do!(ref Brand { ... })  // Explicit, ref dispatch
a_do!(ref { ... })        // Inferred, ref dispatch
  • Brand (optional): The applicative brand type. When omitted, the brand is inferred from container types via InferableBrand.
  • ref (optional): Enables by-reference dispatch. The combining closure receives references (&A, &B, etc.) via RefLift::ref_lift2. Typed binds use the type as-is (include &). Untyped binds get : &_.
  • Bind expressions are evaluated independently (applicative, not monadic).
  • let bindings before any <- are hoisted outside the combinator call.
  • let bindings after a <- are placed inside the combining closure.
  • In explicit mode, bare pure(args) calls are rewritten to pure::<Brand, _>(args).
  • In inferred mode, bare pure(args) calls emit a compile_error!.
  • In inferred mode with 0 binds, a compile_error! is emitted because pure() requires a brand. Write the concrete constructor directly.

§Desugaring

BindsExplicit expansionInferred expansion
0pure::<Brand, _>(final_expr)compile_error!
1explicit::map::<Brand, _, _, _, _>(|x| body, expr)map(|x| body, expr)
N (2-5)explicit::liftN::<Brand, ...>(|x, y, ...| body, ...)liftN(|x, y, ...| body, ...)

§Examples

use fp_library::functions::*;
use fp_macros::a_do;

// Inferred mode: two independent computations combined with lift2
let result = a_do!({
    x <- Some(3);
    y <- Some(4);
    x + y
});
assert_eq!(result, Some(7));

// Expands to:
let result = lift2(|x, y| x + y, Some(3), Some(4));

// Inferred mode: single bind uses map
let result = a_do!({ x <- Some(5); x * 2 });
assert_eq!(result, Some(10));

// Expands to:
let result = map(|x| x * 2, Some(5));
// Explicit mode: zero-bind block uses pure (requires brand)
let result: Option<i32> = a_do!(OptionBrand { 42 });
assert_eq!(result, Some(42));

// Expands to:
let result: Option<i32> = pure::<OptionBrand, _>(42);

// Explicit mode: single bind
let result = a_do!(OptionBrand { x <- Some(5); x * 2 });

// Expands to:
let result = explicit::map::<OptionBrand, _, _, _, _>(|x| x * 2, Some(5));