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