Skip to main content

behavior/
transition.rs

1//! Pure behavior folds from one typed event to explicit transition actions.
2
3use crate::actor::{Address, BirthMode, BirthNodeLogicalHosts, BirthProtocolProduct};
4use crate::effects::{Acted, Actions, LogicalDeliveryProtocols, SendEffects};
5use crate::user_event::UserEvent;
6
7/// Reusable zero-state protocol identity for messages `M` addressed by `A`.
8///
9/// This value has no runtime operations. It lets an actor template publish its
10/// communication signature independently of the concrete state/fold type that
11/// currently implements that signature.
12pub struct MessageProtocol<A: Address, M>(core::marker::PhantomData<fn(A, M)>);
13
14impl<A: Address, M> Protocol for MessageProtocol<A, M> {
15    type Addr = A;
16    type Msg = M;
17}
18
19/// The statically known address and message signature of an actor protocol.
20///
21/// This signature is deliberately independent of [`Behavior`]'s transition
22/// algebra. A [`Recipient`](crate::Recipient) or [`Delivery`](crate::Delivery)
23/// needs to prove only which address namespace and message type it names; it
24/// must not recursively prove the destination's sends, births, phases, or
25/// transition implementation. That separation permits closed static actor
26/// topologies in which a root sends to an actor whose reply path returns to the
27/// same root.
28pub trait Protocol {
29    type Addr: Address;
30    type Msg;
31}
32
33/// The only successful effect shape admitted by a [`Behavior`] implementation.
34pub type BehaviorActed<B> = Acted<
35    BehaviorAddr<B>,
36    <B as Behavior>::Ph,
37    <B as Behavior>::Sends,
38    <B as Behavior>::Birth,
39    <B as Behavior>::Error,
40>;
41
42/// Address namespace projected from a behavior's stable public protocol.
43pub type BehaviorAddr<B> = <<B as Behavior>::Protocol as Protocol>::Addr;
44
45/// Public message algebra projected from a behavior's stable protocol.
46pub type BehaviorMessage<B> = <<B as Behavior>::Protocol as Protocol>::Msg;
47
48/// Capability for defining one initialization fold.
49///
50/// The constructor is private: only the lifecycle boundary can issue this
51/// capability, exactly once for an owned behavior value.
52pub struct InitializationTurn {
53    #[allow(dead_code, reason = "private field prevents external construction")]
54    private: (),
55}
56
57impl InitializationTurn {
58    pub(crate) const fn new() -> Self {
59        Self { private: () }
60    }
61}
62
63/// Capability for defining one active mailbox fold.
64///
65/// Values are issued only by the lifecycle and wrapper-composition boundaries.
66pub struct ActiveTurn {
67    #[allow(dead_code, reason = "private field prevents external construction")]
68    private: (),
69}
70
71impl ActiveTurn {
72    pub(crate) const fn new() -> Self {
73        Self { private: () }
74    }
75}
76
77/// A composed pure behavior. `Event` is the complete accepted input algebra;
78/// the separately declared [`Protocol`] is stable public destination identity,
79/// and every successful transition returns the declared [`Actions`] value.
80///
81/// The public protocol must exactly match the address and user-message lane:
82///
83/// ```compile_fail
84/// use behavior::{Actions, ActiveTurn, Behavior, BehaviorActed, MailAddr, MessageProtocol,
85///     Never, NoBirths, Protocol, User};
86/// struct Wrong;
87/// impl Protocol for Wrong {
88///     type Addr = MailAddr;
89///     type Msg = String;
90/// }
91/// struct Counter;
92/// impl Protocol for Counter {
93///     type Addr = MailAddr;
94///     type Msg = u8;
95/// }
96/// impl Behavior for Counter {
97///     type Protocol = Wrong;
98///     type Event = User<MailAddr, u8>;
99///     type Sends = Vec<Never>;
100///     type Ph = Never;
101///     type Error = Never;
102///     type Birth = NoBirths;
103///     fn transition(&mut self, _: ActiveTurn, _: Self::Event) -> BehaviorActed<Self> {
104///         Ok(Actions::cont())
105///     }
106/// }
107/// ```
108pub trait Behavior {
109    /// Stable public communication identity owned by this actor template.
110    ///
111    /// Behavior wrappers must preserve this type unless their documented
112    /// purpose is to adapt the public message protocol. The equality bound
113    /// prevents a behavior from consuming a different address or user-message
114    /// signature than the protocol established for its actor identity.
115    type Protocol: Protocol;
116
117    type Event: UserEvent<Addr = BehaviorAddr<Self>, Message = BehaviorMessage<Self>>;
118    type Sends: SendEffects + crate::SendsFor<Self::Event>;
119    type Ph;
120    type Error;
121    type Birth: BirthMode;
122
123    /// Produce initialization actions before the first event is accepted.
124    ///
125    /// # Errors
126    ///
127    /// Returns the behavior's declared controlled initialization failure.
128    fn init(&mut self, _turn: InitializationTurn) -> BehaviorActed<Self>
129    where
130        Self: Sized,
131    {
132        Ok(Actions::cont())
133    }
134
135    /// Fold exactly one event into explicit actions and the next behavior.
136    ///
137    /// # Errors
138    ///
139    /// Returns the behavior's declared controlled transition failure.
140    fn transition(&mut self, _turn: ActiveTurn, event: Self::Event) -> BehaviorActed<Self>;
141
142    /// Apply one statically dispatched construction layer.
143    ///
144    /// The returned concrete behavior is inferred from `layer`; no trait
145    /// object or erased behavior is introduced. Construction performs no
146    /// actor effect and does not run either initialization or a transition.
147    #[must_use]
148    fn layer<L>(self, layer: L) -> L::Output
149    where
150        Self: Sized,
151        L: BehaviorLayer<Self>,
152    {
153        BehaviorLayer::layer(&layer, self)
154    }
155}
156
157/// Complete static logical-host requirement of one composed behavior.
158///
159/// The product is derived from the behavior's concrete sends algebra and from
160/// every behavior reachable through its transitive birth algebra. Only
161/// intentional logical [`Delivery`](crate::Delivery) lanes contribute a
162/// protocol. Exact established recipients, creator-local children and inputs,
163/// and interpreter requests do not require a logical host. Repeated protocol
164/// occurrences are retained in the existing structural birth-protocol
165/// product; this trait performs no normalization or runtime lookup.
166///
167/// A generic application owner can consume `LogicalHosts` recursively and
168/// require its own concrete `Hosts<P>` proof for every element. The product is
169/// evidence only: it creates no actor space and does not alter [`Actions`].
170pub trait LogicalHostRequirements: Behavior {
171    /// Ordered, duplicate-preserving protocols requiring logical hosting.
172    type LogicalHosts: BirthProtocolProduct;
173}
174
175impl<B> LogicalHostRequirements for B
176where
177    B: Behavior,
178    B::Sends: LogicalDeliveryProtocols,
179    <B::Birth as BirthMode>::Child: BirthNodeLogicalHosts,
180{
181    type LogicalHosts =
182        <<B::Sends as LogicalDeliveryProtocols>::Protocols as BirthProtocolProduct>::Append<
183            <<B::Birth as BirthMode>::Child as BirthNodeLogicalHosts>::LogicalHosts,
184        >;
185}
186
187/// Static construction from one concrete behavior to another.
188///
189/// This is Bombay's generic consumer contract for Tower-like behavior
190/// composition. `Output` remains a fully concrete [`Behavior`], so its public
191/// protocol, complete event sum, named send product, birth algebra,
192/// initialization fold, phase, error, and next-behavior decision all remain
193/// available through ordinary associated types. A layer value performs no
194/// send, creation, initialization, transition, or runtime lookup; it only owns
195/// the information needed to construct its output.
196///
197/// The output remains in the input behavior's address namespace. It may
198/// preserve or deliberately adapt the public protocol, event, sends, births,
199/// phase, and error only as documented by the concrete output behavior. This
200/// trait does not assert that an arbitrary closure is topology-transparent;
201/// that semantic law belongs to the concrete transformation and its tests.
202///
203/// Closures implement this contract directly, allowing existing concrete
204/// wrapper constructors to compose without a parallel catalogue of marker or
205/// configuration-only `*Layer` types:
206///
207/// ```
208/// use behavior::{Actions, Behavior, BehaviorActed, BehaviorLayer, MailAddr,
209///     Never, NoBirths, Protocol, User};
210///
211/// struct Inner;
212/// impl Protocol for Inner { type Addr = MailAddr; type Msg = (); }
213/// impl Behavior for Inner {
214///     type Protocol = Self;
215///     type Event = User<MailAddr, ()>;
216///     type Sends = Vec<Never>;
217///     type Ph = Never;
218///     type Error = Never;
219///     type Birth = NoBirths;
220///     fn transition(&mut self, _: behavior::ActiveTurn, _: Self::Event)
221///         -> BehaviorActed<Self> { Ok(Actions::cont()) }
222/// }
223///
224/// fn apply<B, L>(behavior: B, layer: L) -> L::Output
225/// where
226///     B: Behavior,
227///     L: BehaviorLayer<B>,
228/// {
229///     layer.layer(behavior)
230/// }
231///
232/// let _: Inner = apply(Inner, core::convert::identity::<Inner>);
233/// ```
234pub trait BehaviorLayer<B: Behavior>
235where
236    <Self::Output as Behavior>::Protocol: Protocol<Addr = BehaviorAddr<B>>,
237{
238    /// Fully concrete behavior constructed by this layer.
239    type Output: Behavior;
240
241    /// Construct one concrete composition from `inner`.
242    ///
243    /// The layer is borrowed so a topology owner can apply the same stateless
244    /// or configuration-bearing construction law to every child it owns.
245    #[must_use]
246    fn layer(&self, inner: B) -> Self::Output;
247}
248
249impl<B, F, Output> BehaviorLayer<B> for F
250where
251    B: Behavior,
252    F: Fn(B) -> Output,
253    Output: Behavior,
254    Output::Protocol: Protocol<Addr = BehaviorAddr<B>>,
255{
256    type Output = Output;
257
258    fn layer(&self, inner: B) -> Self::Output {
259        self(inner)
260    }
261}
262
263/// Static projection from a composed behavior to its authored base behavior.
264///
265/// Wrappers preserve this associated type, so inspection never depends on
266/// wrapper nesting depth or a positional path.
267pub trait BehaviorBase {
268    type Base;
269
270    fn base(&self) -> &Self::Base;
271}
272
273/// Initialize an inner behavior owned by a semantic composition.
274///
275/// This is Bombay's derived, canonical boundary for wrapper composition; it is
276/// not an additional actor-model operation. It invokes the inner initialization
277/// fold exactly once and returns its complete typed action value without
278/// inspecting or transforming it. It does not execute a runtime turn,
279/// interpret effects, or provide an alternate actor executor; top-level runtime
280/// transitions remain the responsibility of the runtime's machine adapter.
281///
282/// # Errors
283///
284/// Returns the inner behavior's controlled transition failure unchanged.
285#[doc(hidden)]
286pub fn initialize<B: Behavior>(behavior: &mut B) -> BehaviorActed<B> {
287    B::init(behavior, InitializationTurn::new())
288}
289
290/// Fold one event through an inner behavior owned by a semantic wrapper.
291///
292/// This invokes the inner deterministic fold exactly once and returns its
293/// complete typed action value without interpreting it.
294///
295/// # Errors
296///
297/// Returns the inner behavior's controlled transition failure unchanged.
298#[doc(hidden)]
299pub fn delegate_transition<B: Behavior>(behavior: &mut B, event: B::Event) -> BehaviorActed<B> {
300    B::transition(behavior, ActiveTurn::new(), event)
301}