From ab2a6f06e629a35c7cf16018881899d22c537fd3 Mon Sep 17 00:00:00 2001 From: Marius Eriksen Date: Tue, 7 Oct 2025 13:37:02 -0700 Subject: [PATCH 1/2] [hyperactor] alias! -> behavior! The name `alias!` has bothered me for a long time. It does not create an alias, but rather it defines a collection of messages represented by a single newtype -- Erlang calls this concept a "behavior", so we adopt it here. Differential Revision: [D84091583](https://our.internmc.facebook.com/intern/diff/D84091583/) **NOTE FOR REVIEWERS**: This PR has internal Meta-specific changes or comments, please review them on [Phabricator](https://our.internmc.facebook.com/intern/diff/D84091583/)! [ghstack-poisoned] --- .../books/hyperactor-book/src/SUMMARY.md | 2 +- .../src/macros/{alias.md => behavior.md} | 33 ++++++++-------- hyperactor/example/derive.rs | 4 +- hyperactor/src/actor.rs | 12 +++--- hyperactor/src/lib.rs | 2 +- hyperactor_macros/src/lib.rs | 39 ++++++++++--------- hyperactor_macros/tests/export.rs | 2 +- hyperactor_mesh/src/bootstrap.rs | 2 +- hyperactor_multiprocess/src/supervision.rs | 6 +-- monarch_messages/src/client.rs | 2 +- monarch_messages/src/controller.rs | 2 +- monarch_messages/src/debugger.rs | 2 +- monarch_messages/src/worker.rs | 2 +- 13 files changed, 57 insertions(+), 53 deletions(-) rename docs/source/books/hyperactor-book/src/macros/{alias.md => behavior.md} (72%) diff --git a/docs/source/books/hyperactor-book/src/SUMMARY.md b/docs/source/books/hyperactor-book/src/SUMMARY.md index 334b01176..717c99555 100644 --- a/docs/source/books/hyperactor-book/src/SUMMARY.md +++ b/docs/source/books/hyperactor-book/src/SUMMARY.md @@ -47,6 +47,6 @@ - [`#[derive(Named)]`](macros/named.md) - [`#[export]`](macros/export.md) - [`#[forward]`](macros/forward.md) - - [`#[alias]`](macros/alias.md) + - [`#[behavior]`](macros/behavior.md) - [Appendix](appendix/index.md) - [Typed Message Lifecycle](appendix/lifecycle.md) diff --git a/docs/source/books/hyperactor-book/src/macros/alias.md b/docs/source/books/hyperactor-book/src/macros/behavior.md similarity index 72% rename from docs/source/books/hyperactor-book/src/macros/alias.md rename to docs/source/books/hyperactor-book/src/macros/behavior.md index 3e69d399c..531d90b57 100644 --- a/docs/source/books/hyperactor-book/src/macros/alias.md +++ b/docs/source/books/hyperactor-book/src/macros/behavior.md @@ -1,11 +1,12 @@ -# `#[alias]` +# `#[behavior]` -The `#[alias]` macro defines a façade actor type that exposes only a selected set of messages. -This allows you to hand out **stable or restricted APIs** without tying clients to the full concrete actor type. +The `#[behavior]` macro defines a type which represents an actor that handles a set of messages. +Behaviors allows you to define and hand out **stable or restricted APIs** without tying clients +to the full concrete actor type. -### Defining an alias +### Defining a behavior -An alias groups together one or more message enums or structs: +A behavior groups together one or more message enums or structs: ```rust #[derive(Handler)] @@ -28,8 +29,8 @@ struct GetItemCount { reply: OncePortRef, } -// Define an alias actor `ShoppingApi` that exposes exactly these messages. -hyperactor::alias!( +// Define a behavior `ShoppingApi` that exposes exactly these messages. +hyperactor::behavior!( ShoppingApi, ShoppingList, ClearList, @@ -37,14 +38,14 @@ hyperactor::alias!( ); ``` -The alias can include: +The behavior can include any type of message: - Enums (e.g. `ShoppingList`) - Struct messages (e.g. `ClearList`, `GetItemCount`) - Generic messages, with concrete type parameters bound at the alias site. -### Using an alias +### Using a behavior -After spawning the real actor, re-type its id as the alias: +After spawning the real actor, re-type its id as the behavior: ```rust let mut proc = Proc::local(); @@ -69,14 +70,14 @@ println!("items containing 'dairy': {n}"); shopping_api.clear_list(&client, "end of session".into()).await?; ``` -> **Note:** `alias!` does *not* rename methods. It authorizes those calls on +> **Note:** `behavior!` does *not* rename methods. It authorizes those calls on > `ActorRef` if and only if the message type was included. > **Note:** `attest` is a low-level escape hatch. It asserts that a raw -> `ActorId` is valid for the alias type. This example uses it only because +> `ActorId` is valid for the behavior. This example uses it only because > we just spawned the actor and know the id is safe. > In general, prefer higher-level APIs (such as `Proc` utilities) for -> constructing alias references, and use `attest` sparingly. +> constructing behavior references, and use `attest` sparingly. ### Generated code (excerpt) @@ -108,12 +109,12 @@ impl hyperactor::actor::RemoteHandles> for ShoppingApi {} ### Capability slicing -If a message type is not listed in the alias, trying to call it will fail at compile time: +If a message type is not listed in the behavior, trying to call it will fail at compile time: ```rust -// If ClearList were omitted from the alias: +// If ClearList were omitted from the behavior: shopping_api.clear_list(&client, "...").await?; // ^ error: the trait bound `ShoppingApi: RemoteHandles` is not satisfied ``` -This makes `alias!` a useful tool for **compile-time capability control**. +This makes `behavior!` a useful tool for **compile-time capability control**. diff --git a/hyperactor/example/derive.rs b/hyperactor/example/derive.rs index feaa2417c..f9b92b5d3 100644 --- a/hyperactor/example/derive.rs +++ b/hyperactor/example/derive.rs @@ -129,10 +129,10 @@ impl GetItemCountHandler for ShoppingListActor { } } -// Define an alias actor `ShoppingApi`. Clients can use +// Define a behavior `ShoppingApi`. Clients can use // `ActorRef` instead of referencing the concrete // `ShoppingListActor` directly. -hyperactor::alias!(ShoppingApi, ShoppingList, ClearList, GetItemCount,); +hyperactor::behavior!(ShoppingApi, ShoppingList, ClearList, GetItemCount,); #[tokio::main] async fn main() -> Result<(), anyhow::Error> { diff --git a/hyperactor/src/actor.rs b/hyperactor/src/actor.rs index 90dcdd96d..4e47a8bdd 100644 --- a/hyperactor/src/actor.rs +++ b/hyperactor/src/actor.rs @@ -684,7 +684,7 @@ impl Clone for ActorHandle { /// remote references across process boundaries. /// /// It is not limited to concrete [`Actor`] implementations. For -/// example, façade types generated by [`alias!`] implement +/// example, façade types generated by [`behavior!`] implement /// `Referable` so that you can hand out restricted or stable APIs /// while still using the same remote messaging machinery. /// @@ -695,7 +695,7 @@ impl Clone for ActorHandle { /// boundaries. /// /// In contrast, [`RemotableActor`] is the trait that marks *actors* -/// that can actually be **spawned remotely**. An alias type may be a +/// that can actually be **spawned remotely**. A behavior may be a /// `Referable` but is never a `RemotableActor`. pub trait Referable: Named + Send + Sync {} @@ -989,7 +989,7 @@ mod tests { _cx: &Context, message: String, ) -> Result<(), anyhow::Error> { - let mut vals = self.0.lock().unwrap(); + let mut behavior = self.0.lock().unwrap(); vals.1 = message; Ok(()) } @@ -1028,15 +1028,15 @@ mod tests { } #[tokio::test] - async fn test_ref_alias() { + async fn test_ref_behavior() { let test = MultiValuesTest::new().await; test.send(123u64); test.send("foo".to_string()); - hyperactor::alias!(MyActorAlias, u64, String); + hyperactor::behavior!(MyActorBehavior, u64, String); - let myref: ActorRef = test.handle.bind(); + let myref: ActorRef = test.handle.bind(); myref.port().send(&test.client, "biz".to_string()).unwrap(); myref.port().send(&test.client, 999u64).unwrap(); diff --git a/hyperactor/src/lib.rs b/hyperactor/src/lib.rs index 8a30e9c51..6e4782785 100644 --- a/hyperactor/src/lib.rs +++ b/hyperactor/src/lib.rs @@ -127,7 +127,7 @@ pub use hyperactor_macros::RefClient; #[doc(inline)] pub use hyperactor_macros::Unbind; #[doc(inline)] -pub use hyperactor_macros::alias; +pub use hyperactor_macros::behavior; #[doc(inline)] pub use hyperactor_macros::export; #[doc(inline)] diff --git a/hyperactor_macros/src/lib.rs b/hyperactor_macros/src/lib.rs index 6e4a909ad..20e41b214 100644 --- a/hyperactor_macros/src/lib.rs +++ b/hyperactor_macros/src/lib.rs @@ -1603,31 +1603,31 @@ pub fn export(attr: TokenStream, item: TokenStream) -> TokenStream { TokenStream::from(expanded) } -/// Represents the full input to [`fn alias`]. -struct AliasInput { - alias: Ident, +/// Represents the full input to [`fn behavior`]. +struct BehaviorInput { + behavior: Ident, handlers: Vec, } -impl syn::parse::Parse for AliasInput { +impl syn::parse::Parse for BehaviorInput { fn parse(input: syn::parse::ParseStream) -> syn::Result { - let alias: Ident = input.parse()?; + let behavior: Ident = input.parse()?; let _: Token![,] = input.parse()?; let raw_handlers = input.parse_terminated(HandlerSpec::parse, Token![,])?; let handlers = raw_handlers.into_iter().collect(); - Ok(AliasInput { alias, handlers }) + Ok(BehaviorInput { behavior, handlers }) } } -/// Create a [`Referable`] handling a specific set of message types. -/// This is used to create an [`ActorRef`] without having to depend on the +/// Create a [`Referable`] definition, handling a specific set of message types. +/// Behaviors are used to create an [`ActorRef`] without having to depend on the /// actor's implementation. If the message type need to be cast, add `castable` -/// flag to those types. e.g. the following example creats an alias with 5 +/// flag to those types. e.g. the following example creates a behavior with 5 /// message types, and 4 of which need to be cast. /// /// ``` -/// hyperactor::alias!( -/// TestActorAlias, +/// hyperactor::behavior!( +/// TestActorBehavior, /// TestMessage { castable = true }, /// () {castable = true }, /// MyGeneric<()> {castable = true }, @@ -1635,17 +1635,20 @@ impl syn::parse::Parse for AliasInput { /// ); /// ``` #[proc_macro] -pub fn alias(input: TokenStream) -> TokenStream { - let AliasInput { alias, handlers } = parse_macro_input!(input as AliasInput); +pub fn behavior(input: TokenStream) -> TokenStream { + let BehaviorInput { + behavior: behavior, + handlers, + } = parse_macro_input!(input as BehaviorInput); let tys = HandlerSpec::add_indexed(handlers); let expanded = quote! { - #[doc = "The generated alias struct."] + #[doc = "The generated behavior struct."] #[derive(Debug, hyperactor::Named, serde::Serialize, serde::Deserialize)] - pub struct #alias; - impl hyperactor::actor::Referable for #alias {} + pub struct #behavior; + impl hyperactor::actor::Referable for #behavior {} - impl hyperactor::actor::Binds for #alias + impl hyperactor::actor::Binds for #behavior where A: hyperactor::Actor #(+ hyperactor::Handler<#tys>)* { fn bind(ports: &hyperactor::proc::Ports) { @@ -1656,7 +1659,7 @@ pub fn alias(input: TokenStream) -> TokenStream { } #( - impl hyperactor::actor::RemoteHandles<#tys> for #alias {} + impl hyperactor::actor::RemoteHandles<#tys> for #behavior {} )* }; diff --git a/hyperactor_macros/tests/export.rs b/hyperactor_macros/tests/export.rs index 82c4c5aef..81d22d4f8 100644 --- a/hyperactor_macros/tests/export.rs +++ b/hyperactor_macros/tests/export.rs @@ -85,7 +85,7 @@ impl Handler for TestActor { } } -hyperactor::alias!( +hyperactor::behavior!( TestActorAlias, TestMessage { cast = true }, () { cast = true }, diff --git a/hyperactor_mesh/src/bootstrap.rs b/hyperactor_mesh/src/bootstrap.rs index c6231d1a9..ba949bd50 100644 --- a/hyperactor_mesh/src/bootstrap.rs +++ b/hyperactor_mesh/src/bootstrap.rs @@ -179,7 +179,7 @@ async fn exit_if_missed_heartbeat(bootstrap_index: usize, bootstrap_addr: Channe } #[macro_export] -macro_rules! ok { +macro_rules! ok {re ($expr:expr $(,)?) => { match $expr { Ok(value) => value, diff --git a/hyperactor_multiprocess/src/supervision.rs b/hyperactor_multiprocess/src/supervision.rs index bbb08fc1e..ded8f44fe 100644 --- a/hyperactor_multiprocess/src/supervision.rs +++ b/hyperactor_multiprocess/src/supervision.rs @@ -133,6 +133,6 @@ impl ProcSupervisionState { } } -hyperactor::alias!(ProcSupervisor, ProcSupervisionMessage); // For proc supervisor to implement (e.g. system actor) -hyperactor::alias!(WorldSupervisor, WorldSupervisionMessage); // For world supervisor to implement (e.g. system actor) -hyperactor::alias!(SupervisionClient, WorldSupervisionState); // For the end receiver of supervision events to implement (e.g. client) +hyperactor::behavior!(ProcSupervisor, ProcSupervisionMessage); // For proc supervisor to implement (e.g. system actor) +hyperactor::behavior!(WorldSupervisor, WorldSupervisionMessage); // For world supervisor to implement (e.g. system actor) +hyperactor::behavior!(SupervisionClient, WorldSupervisionState); // For the end receiver of supervision events to implement (e.g. client) diff --git a/monarch_messages/src/client.rs b/monarch_messages/src/client.rs index c50376843..d0a961b89 100644 --- a/monarch_messages/src/client.rs +++ b/monarch_messages/src/client.rs @@ -95,4 +95,4 @@ pub enum ClientMessage { }, } -hyperactor::alias!(ClientActor, ClientMessage); +hyperactor::behavior!(ClientActor, ClientMessage); diff --git a/monarch_messages/src/controller.rs b/monarch_messages/src/controller.rs index 7c40130f5..8d59ca592 100644 --- a/monarch_messages/src/controller.rs +++ b/monarch_messages/src/controller.rs @@ -237,4 +237,4 @@ pub enum ControllerMessage { }, } -hyperactor::alias!(ControllerActor, ControllerMessage); +hyperactor::behavior!(ControllerActor, ControllerMessage); diff --git a/monarch_messages/src/debugger.rs b/monarch_messages/src/debugger.rs index 633e38cfc..7794cf53b 100644 --- a/monarch_messages/src/debugger.rs +++ b/monarch_messages/src/debugger.rs @@ -55,7 +55,7 @@ pub enum DebuggerMessage { Action { action: DebuggerAction }, } -hyperactor::alias!( +hyperactor::behavior!( DebuggerActor, DebuggerMessage { cast = true }, ); diff --git a/monarch_messages/src/worker.rs b/monarch_messages/src/worker.rs index b9d7f1a06..255571b90 100644 --- a/monarch_messages/src/worker.rs +++ b/monarch_messages/src/worker.rs @@ -941,7 +941,7 @@ pub struct WorkerParams { pub controller_actor: ActorRef, } -hyperactor::alias!( +hyperactor::behavior!( WorkerActor, WorkerMessage { cast = true }, ); From 5a6f4ed78ae40284c6a1eacf87735657a26dc27c Mon Sep 17 00:00:00 2001 From: Marius Eriksen Date: Tue, 7 Oct 2025 13:59:40 -0700 Subject: [PATCH 2/2] Update on "[hyperactor] alias! -> behavior!" The name `alias!` has bothered me for a long time. It does not create an alias, but rather it defines a collection of messages represented by a single newtype -- Erlang calls this concept a "behavior", so we adopt it here. Differential Revision: [D84091583](https://our.internmc.facebook.com/intern/diff/D84091583/) **NOTE FOR REVIEWERS**: This PR has internal Meta-specific changes or comments, please review them on [Phabricator](https://our.internmc.facebook.com/intern/diff/D84091583/)! [ghstack-poisoned] --- hyperactor/src/actor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperactor/src/actor.rs b/hyperactor/src/actor.rs index 4e47a8bdd..e728d6e7f 100644 --- a/hyperactor/src/actor.rs +++ b/hyperactor/src/actor.rs @@ -989,7 +989,7 @@ mod tests { _cx: &Context, message: String, ) -> Result<(), anyhow::Error> { - let mut behavior = self.0.lock().unwrap(); + let mut vals = self.0.lock().unwrap(); vals.1 = message; Ok(()) }