Crates.io | acton-reactive |
lib.rs | acton-reactive |
version | 1.1.1-beta.1 |
source | src |
created_at | 2024-10-03 23:56:27.567518 |
updated_at | 2024-10-04 16:34:59.058307 |
description | Acton Reactive is the main crate of the Acton framework, designed for building reactive, event-driven, and asynchronous systems. It provides intuitive abstractions to make working with distributed agents seamless and efficient. |
homepage | https://github.com/Govcraft/acton-reactive |
repository | https://github.com/Govcraft/acton-reactive |
max_upload_size | |
id | 1395821 |
size | 138,426 |
The Acton Reactive Application Framework provides an efficient way to build fast, reactive Rust applications. Designed around an agent-based model, it simplifies concurrency and allows developers to focus on writing scalable, maintainable code. Acton gets its name from the fact that it "acts on" messages you define.
To get started with the Acton framework, add the following to your Cargo.toml
:
[dependencies]
acton_reactive = { version = "1.1.0-beta.1" }
Here's a simple example of how to create and use agents in the Acton framework:
use acton_reactive::prelude::*;
Default
and Debug
traits. Here's an example of a basic agent:// agents must derive these traits
#[derive(Debug, Default)]
struct ABasicAgent {
some_state: usize,
}
Debug
and Clone
traits. Here's an example:// messages must derive these traits
#[derive(Debug, Clone)]
struct PingMsg;
acton_message
macro:
// or save a few keystrokes and use the handy macro
#[acton_message]
struct PongMsg;
#[acton_message]
struct BuhByeMsg;
ActonApp
struct is used to launch the Acton system. Here's an example of launching the Acton system and creating an agent:
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut app = ActonApp::launch();
let mut the_agent = app.new_agent::<ABasicAgent>().await;
the_agent
.act_on::<PingMsg>(|agent, context| {
println!("Pinged. You can mutate me!");
agent.model.some_state += 1;
// you can reply to the sender (in this case, the agent itself)
let envelope = context.reply_envelope();
// every handler must return a boxed future
Box::pin(async move {
envelope.send(PongMsg).await;
})
})
Handlers can be chained together to handle multiple message types. Here's an example of chaining handlers:
.act_on::<PongMsg>(|agent, _envelope| {
println!("I got ponged!");
agent.model.some_state += 1;
//if you're replying to the same agent, you can use the agent's handle
let handle = agent.handle().clone(); // handle clones are cheap and need to be done when moving into the async boundary
// if you find the box pin syntax confusing, you use this helper function
AgentReply::from_async(async move {
handle.send(BuhByeMsg).await;
})
})
.act_on::<BuhByeMsg>(|agent, envelope| {
println!("Thanks for all the fish! Buh Bye!");
//if you don't have any async work to do, you can reply with an empty boxed future
//or just use this function
AgentReply::immediate()
})
.after_stop(|agent| {
println!("Agent stopped with state value: {}", agent.model.some_state);
debug_assert_eq!(agent.model.some_state, 2);
AgentReply::immediate()
});
let the_agent = the_agent.start().await;
the_agent.send(PingMsg).await;
// shutdown tries to gracefully stop all agents and their children
app.shutdown_all().await?;
Ok(())
}
You can view this example and run it from the examples basic
folder.
For more advanced usage, check out the lifecycles, broadcast, and fruit_market examples, which demonstrate other messaging patterns and system-wide coordination across agents.
While Acton is, at its core, an actor framework, I’ve chosen to use the term "agent" to focus on clarity and accessibility. The word "actor" comes with a lot of technical baggage from traditional actor models like Akka and Erlang, which may seem complex or intimidating to some developers.
The term "agent" emphasizes the framework’s core principle: it acts on messages in a straightforward, scalable way. By avoiding the technical connotations of "actor," I hope to make Acton more approachable and easier to understand, especially for those who may be new to concurrency patterns. In essence, agents in Acton do the same things that actors do in other systems—handle state, process messages, and operate concurrently—but with a focus on flexibility and simplicity.
Functionally, agents in Acton perform the same role as actors in other frameworks. They:
Maintain independent state. Process incoming messages asynchronously. Operate in a concurrent, non-blocking environment. The difference is mainly in terminology. I want to make Acton more accessible to developers who may not be familiar with the traditional actor model. The term "agent" avoids preconceptions and focuses on what matters: acting on messages efficiently, without introducing unnecessary complexity.
Acton takes inspiration from traditional actor models, but it’s designed to be more flexible and user-friendly. It still provides the core benefits of actor-based concurrency—message passing, state isolation, and non-blocking processing—while also leveraging modern Rust features like async/await to ensure performance and safety.
In short, Acton is an actor framework by design but optimized for modern Rust applications and with a focus on practicality rather than adhering to a strict actor model.
Contributions to the Acton Reactive Application Framework are welcome! Please feel free to submit issues, fork the repository, and send pull requests!
This project is licensed under both the MIT and Apache-2.0 licenses. See the LICENSE-MIT
and LICENSE-APACHE
files for details.