alux-ext 0.2.5

Meaning-first Rust extensions with first-order operation defunctionalization
Documentation
# alux-ext

`alux-ext` gives you one attribute, `#[ext]`, that does two things: it writes the boilerplate needed
to add methods to types you do not own, and — when you ask for it — it turns each of those methods
into a *value* that stands for calling the method later.

The second part is the unusual one, so this README goes through it slowly: the problem being solved,
the code you write, the code the macro adds, and how the two fit together.

## The problem the `ext` attribute solves

Rust does not let you add a method to a type defined in another crate (see the [orphan rules](https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules)). The standard workaround is an
*extension trait* ([RFC 445](https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html)): declare a trait with the method, then implement it for the types you want. That
means writing every signature twice — once in the trait, once in the implementation — and keeping
them in sync forever.

[`extend::ext`](https://github.com/davidpdrsn/extend) removes that duplication. You write one
`impl` block that looks inherent, and the macro produces the trait and its blanket implementation:

```rust ignore
// You write this. It looks like an inherent impl, but `This` is a type parameter, so it applies to
// every type satisfying the bound rather than to one concrete type.
#[ext(name = ValueExt)]
impl<This> This
where
    This: ValueAlg,
{
    fn describe(&self) -> String {
        self.value().to_string()
    }
}

// The macro writes this for you: a trait carrying the signature, plus one blanket impl carrying the
// body. Any type implementing `ValueAlg` now has `.describe()` wherever `ValueExt` is in scope.
trait ValueExt<This> { fn describe(&self) -> String; }
impl<This> ValueExt<This> for This where This: ValueAlg { /* the body you wrote */ }
```

`alux_ext::ext` takes the same arguments as `extend::ext`: `name`, `supertraits`, and a visibility written before `impl`.

## What the attribute generates

From this block:

```rust ignore
/// The operations a chunked body derives.
#[ext(name = ChunksExt)]
pub impl<This> This
where
    This: ChunksAlg,
{
    /// Reads every chunk.
    async fn gathered(mut self) -> Result<Vec<This::Chunk>, This::Error> {
        /* body */
    }
}
```

it generates:

```rust ignore
/// The operations a chunked body derives.
pub trait ChunksExt<This>
where
    This: ChunksAlg,
{
    /// Reads every chunk.
    fn gathered(self) -> impl core::future::Future<Output = Result<Vec<This::Chunk>, This::Error>>;
}

/// The operations a chunked body derives.
impl<This> ChunksExt<This> for This
where
    This: ChunksAlg,
{
    /// Reads every chunk.
    async fn gathered(mut self) -> Result<Vec<This::Chunk>, This::Error> {
        /* body */
    }
}
```

The trait gets the block's generics and `where` clause, each method's signature, and each const's type. The impl gets the same generics and `where` clause, plus the bodies and const values. Attributes written on the block, such as its documentation, go on both the trait and the impl. Attributes written on a method go on that method in both. `supertraits` becomes the trait's supertraits.

The trait declaration differs from what you wrote in three ways:

1. **`async fn` becomes a future.** The trait declares `fn m(..) -> impl Future<Output = T>` in place of `async fn m(..) -> T`. A caller can name and bound that return type, and the [`async_fn_in_trait`]https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#async-fn-in-trait lint has nothing to warn about. The impl keeps `async fn` as you wrote it.
2. **`mut` and patterns are dropped.** A declaration has no body, so `mut self`, `mut x` and `ref x` are declared as `self` and `x`, and a destructuring pattern such as `(a, b): (u32, u32)` is declared as `_: (u32, u32)`. The impl keeps the bindings as you wrote them.
3. **`#[inline]` is dropped.** It applies to a body, so it stays only on the impl.

Everything else is copied as written, including `Self`, exactly as `extend::ext` does. `Self` in the trait is the implementing type with no bounds, so `Self::Chunk` does not compile there. Write `This::Chunk`, which the block's `where` clause resolves.

`name` is optional; without it the trait is named after the carrier's first trait bound that is not a basic Rust trait such as `Send` or `Clone` (`ChunksAlgExt`), its first bound when all are basic (`SendExt`), or the carrier itself when it has none (`ThisExt`).

### Requiring a `Send` future

`async fn` cannot say that its future is `Send`. To require it, write the method as a future:

```rust ignore
fn gathered(mut self) -> impl Future<Output = Result<Vec<This::Chunk>, This::Error>> + Send {
    async move { /* body */ }
}
```

The trait declares exactly that signature, `+ Send` included. When the body is a single `async` block, the impl states it as `async fn gathered(mut self) -> Result<..>` with that block's contents as the body. A body that is anything else, such as a call returning another future, is copied as written.

### With `defunc`

`defunc` generates one operation type per method next to the trait and the impl; the next section explains it. `defunc(via = path)` hands the block to that backend, which replaces each method by the constructor of its program. The trait and the impl are then generated as described above.

## What `alux-ext` adds on top

Adding `defunc` to the attribute keeps everything above and generates one extra type per method:

```text
#[ext(name = ValueExt)]           ->  extension trait + blanket impl
#[ext(name = ValueExt, defunc)]   ->  extension trait + blanket impl + operation
```

An *operation* is a value that means "apply this method." It records the context the method needs,
the method's arguments as a tuple, the argument names as written in the source, and the result type.
Nothing is executed when you build one.

Why bother, when Rust already has function pointers and closures? Because these methods are generic
over an abstract interpreter and return abstract associated types, and such a method cannot be stored
uniformly in a table, list, or program tree. A zero-sized type per method can. That is what lets
`alux-http` and `alux-jsonrpc` hold your operations inside a route table or a method list, then hand
the whole thing to a framework later. The technique has a name — defunctionalization — and is
explained in the guidelines linked at the end.

## Step 1: the code you write

```rust ignore
use alux_ext::ext;

// You write this: a capability trait stating what the outside world must provide. It is the only
// place the actual value comes from.
trait ValueAlg {
    fn value(&self) -> u32;
}

// You write this too: a derived method, defined purely in terms of the capability above. `defunc`
// asks for the operation type in addition to the normal extension trait.
#[ext(name = ValueExt, defunc)]
impl<This> This
where
    This: ValueAlg,
{
    async fn value_plus(&self, increment: u32) -> u32 {
        self.value() + increment
    }
}

// You write this as well, wherever the real value lives: a concrete type answering the capability.
// It knows nothing about extensions, operations, or transports.
struct Value(u32);

impl ValueAlg for Value {
    fn value(&self) -> u32 {
        self.0
    }
}
```

That is the whole authored surface. Note what is absent: no struct holding state, no trait object, no
registry, no framework, and no repetition of the argument or return types.

## Step 2: the code the macro adds

The expansion below is real output with only the `#[allow(...)]` noise removed. The first half is the
ordinary extension trait described earlier; the second half is the operation.

```rust ignore
// --- First half: the ordinary extension, as a trait declaring the method and an impl carrying its
// body. ---

// The signature, lifted into a trait so it can apply to types this crate does not own. `async fn` is
// how a body is written, not something a declaration can state, so the declaration states the future
// that body answers with. A caller can name and bound that future, and there is nothing here for
// `async_fn_in_trait` to warn about.
trait ValueExt<This>
where
    This: ValueAlg,
{
    fn value_plus(&self, increment: u32) -> impl core::future::Future<Output = u32>;
}

// The body you wrote, kept as the `async fn` it was written as, implemented for every type
// satisfying the bound. This is what makes `value.value_plus(2).await` compile as an ordinary method
// call. Declaring the future and stating the body are the two halves of one written method.
impl<This> ValueExt<This> for This
where
    This: ValueAlg,
{
    async fn value_plus(&self, increment: u32) -> u32 {
        self.value() + increment
    }
}

// --- Second half: the operation. This is what `defunc` adds. ---

// The value that means "apply `value_plus`". It is zero-sized: `PhantomData` only remembers which
// context type the operation expects, so there is no hidden state to configure or get wrong. The
// context is held only behind a reference, so it may be unsized, a `dyn` capability or a slice.
#[doc(hidden)]
struct ValuePlusOperation<Context: ?Sized>(core::marker::PhantomData<(fn(&Context), fn() -> ())>);

// Because it holds nothing, `Default` is enough to build one. Program declarations rely on this.
#[doc(hidden)]
impl<Context: ?Sized> core::default::Default for ValuePlusOperation<Context> {
    fn default() -> Self {
        Self(core::marker::PhantomData)
    }
}

// The method's signature, now readable as data at compile time: which context it needs, its
// arguments as a tuple in declaration order, and the names you gave those arguments. JSON-RPC named
// parameters read `ARG_NAMES` instead of asking you to list the names a second time.
#[doc(hidden)]
impl<Context> ::alux_ext::OperationAlg for ValuePlusOperation<Context> {
    type Context = Context;
    type Args = (u32,);
    const ARG_NAMES: &'static [&'static str] = &["increment"];
}

// Application, kept separate from the signature. `Handle` is whatever carrier the caller chose that
// borrows the context — `Arc<Context>` for a server, `&Context` for a test — so `Arc`, `Send`, and
// executor concerns live here at the boundary instead of in the method you wrote. The future owns
// each argument, so it is `Send` where they are; the method states nothing about threads.
#[doc(hidden)]
impl<Context, Handle> ::alux_ext::ApplyAlg<Handle, (u32,)> for ValuePlusOperation<Context>
where
    Handle: core::borrow::Borrow<Context> + Send,
    Context: Sync,
    u32: Send,
    Context: ValueAlg,
{
    // Inferred from your return type, so no declaration ever restates it.
    type Output = u32;

    fn apply(
        &self,
        __context: Handle,
        __args: (u32,),
    ) -> impl core::future::Future<Output = Self::Output> + Send {
        async move {
            // The tuple is unpacked back into named arguments...
            let (increment,) = __args;
            // ...and the method you wrote is *called*. The body is never copied, so the operation
            // cannot drift away from the method it denotes.
            <Handle as core::borrow::Borrow<Context>>::borrow(&__context).value_plus(increment).await
        }
    }
}
```

`#[doc(hidden)]` hides the machinery, not the name: downstream code refers to `ValuePlusOperation`,
so that name is public compatibility surface and renaming it is a breaking change.

## Step 3: using both halves

```rust ignore
use alux_ext::ApplyAlg;
use std::sync::Arc;

// You write this: the ordinary call. With the generated `ValueExt` in scope, the method is simply
// there. Most code stays at this level and never mentions operations at all.
let value = Arc::new(Value(40));
assert_eq!(value.value_plus(2).await, 42);

// You write this only when application must become data: the same computation, reached through the
// operation value. `(2,)` is the argument tuple, `value` is the chosen handle.
let operation = ValuePlusOperation::<Value>::default();
assert_eq!(operation.apply(value, (2,)).await, 42);
```

Both lines produce `42`, and that is not a coincidence to be tested per method: the generated `apply`
calls `value_plus`, so agreement is structural. `alux-ext`'s own tests check it once.

## The public surface

Three traits, and the attribute:

| Item | Meaning |
| --- | --- |
| `OperationAlg` | The signature of a reified method: context, argument tuple, argument names |
| `ApplyAlg<Handle, Args>` | Application of that method against a handle, producing `Output` |
| `HandlerContextAlg<Context>` | An interpreter's choice of owned handle (for example `Arc<Context>`) for a context |
| `#[ext]` | The attribute above, with optional `defunc`, `defunc(via = http)`, `defunc(via = jsonrpc)` |

`HandlerContextAlg` exists so that a domain method never mentions ownership. The method takes
`&self`; the interpreter decides that sharing it across requests means `Arc<Context>`.

## When to reify a method

Use `defunc` only when the application must become data — stored in a route or method program,
inspected by another interpreter, or composed before anything runs. An ordinary method call that
composes fine needs no operation type, and adding one buys nothing.

For the reasoning behind all of this, read
[First-order programs](https://alux-network.github.io/alux-programming/rust-dd/first-order-programs.html)
and [Defunctionalization](https://alux-network.github.io/alux-programming/concepts/defunctionalization.html)
in the ALUX programming guidelines.