{"id":37519620,"url":"https://github.com/cloudmark/cats-actors","last_synced_at":"2026-01-16T08:13:27.003Z","repository":{"id":242663580,"uuid":"810203440","full_name":"cloudmark/cats-actors","owner":"cloudmark","description":"Cats Actors framework for building apps which are reactive.  Cats actors uses a conceptual actor model as a higher level abstraction for concurrency.","archived":false,"fork":false,"pushed_at":"2025-05-17T06:46:24.000Z","size":13992,"stargazers_count":121,"open_issues_count":2,"forks_count":12,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-05-17T07:37:19.067Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudmark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-04T08:45:09.000Z","updated_at":"2025-05-17T06:46:28.000Z","dependencies_parsed_at":"2024-06-04T10:02:43.210Z","dependency_job_id":"549bf01e-0588-4ebf-89b1-0a4ab2b39e54","html_url":"https://github.com/cloudmark/cats-actors","commit_stats":null,"previous_names":["cloudmark/cats-actors"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/cloudmark/cats-actors","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudmark%2Fcats-actors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudmark%2Fcats-actors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudmark%2Fcats-actors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudmark%2Fcats-actors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudmark","download_url":"https://codeload.github.com/cloudmark/cats-actors/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudmark%2Fcats-actors/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478047,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T06:30:42.265Z","status":"ssl_error","status_checked_at":"2026-01-16T06:30:16.248Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-16T08:13:26.274Z","updated_at":"2026-01-16T08:13:26.985Z","avatar_url":"https://github.com/cloudmark.png","language":"Scala","funding_links":[],"categories":["\u003ca name=\"Scala\"\u003e\u003c/a\u003eScala"],"sub_categories":[],"readme":"# Cats-Actors\n\n[![Release](https://jitpack.io/v/suprnation/cats-actors.svg)](https://jitpack.io/#suprnation/cats-actors)\n\nCats-Actors 🐱 is a functional programming-based actor system derived from Akka v2.6.21 🚀️\n\n![Cats-Actors Logo](images/logo-small.png)\n\n## Acknowledgments\n\nThis project is a derivative work of [Akka](https://www.akka.io/), developed by Lightbend, Inc. and other contributors.\nSignificant modifications have been made to rewrite the actor system and actor cell to use functional programming\nprinciples and fibers.\n\nThe code follows the same design philosophy as the original Akka source code but has been modified to use Cats and\nCats-Effect libraries. It achieves high concurrency by using actors. This project can be considered an abstraction of\nCats-Effect and FS2, providing an actor-based experience and higher-level abstractions to achieve high concurrency.\n\n## Introduction\n\nActors are the fundamental units of computation in the Actor Model, encapsulating state and behavior. They interact\nexclusively through asynchronous message passing, ensuring their state is accessed sequentially.\n\n### How Actors Communicate\n\n- **Message Passing**: Actors communicate by sending messages to each other, which are queued in their mailboxes and\n  processed one at a time.\n- **Actor References**: Actors use unique references (ActorRef) to send messages.\n\n### Actor Lifecycle\n\n- **Creation**: Actors are created by other actors, forming a hierarchy. Root actors are created by the actor system.\n- **Running**: In the \"RUNNING\" state, actors process messages defined by a receive method.\n- **Stopping**: Actors can be stopped, entering the \"SHUTDOWN\" state, and can perform cleanup tasks.\n- **Restarting**: Actors can be restarted by their parent, with preRestart and postRestart methods for cleanup and\n  reinitialization.\n\n### Supervision and Fault Tolerance\n\n- **Supervision Strategies**: Define how to handle actor failures, ensuring system robustness.\n- **Fault Tolerance**: Isolating state within actors and handling failures through supervision provides a robust\n  framework for building fault-tolerant systems.\n\nFor more details, refer to the [Wikipedia page on the Actor Model](https://en.wikipedia.org/wiki/Actor_model).\n\nCats-Actors is a library designed to help developers build highly concurrent applications using functional programming\nprinciples. It is a fork of Akka 2.6.x, created before the Akka project adopted the Business Source License. The library\nhas been modified to use modern functional programming tools like Cats and Cats-Effect, making it easier to write safe,\nefficient, and scalable code. Cats-Actors allows you to manage multiple tasks simultaneously without the complexity of\ntraditional threading, making it ideal for applications that need to handle a lot of operations at once.\n\n### Why Should I Use Cats-Actors?\n\n- **Functional Programming Principles**\n\n  - **Cats and Cats-Effect Integration**: Supports immutability, pure functions, and referential transparency.\n  - **Fibers**: Offers lightweight concurrency for efficient, scalable applications.\n- **Simplified Concurrency Model**\n\n  - **High Concurrency**: Achieves high concurrency with minimal overhead and complexity.\n- **Fault Tolerance and Supervision**\n\n  - **Supervision Strategies**: Robust, functional programming-based supervision strategies.\n  - **Functional Error Handling**: Makes error handling expressive and easier to reason about.\n- **Modularity and Composability**\n\n  - **Composable Effects**: Enables modular and reusable complex workflows.\n- **Type Safety and Expressiveness**\n\n  - **Type Classes and Higher-Kinded Types**: Provides expressive and type-safe code, reducing runtime errors.\n  - **Fully Typed Actors**: Ensures that actors are fully typed, specifying the types of messages they can receive and reply with, enhancing type safety and reducing runtime errors.\n- **Unique Features**\n\n  - **Most Comprehensive Actor Implementation in the Functional Space**: Cats-Actors implements become/unbecome,\n    supervision, and watch in a functional way, making it the most comprehensive actor implementation in the functional\n    programming ecosystem.\n\nCats-Actors leverages functional programming and the Cats and Cats-Effect libraries for a more efficient, maintainable,\nand expressive framework for building concurrent applications.\n\n\n## Getting Started\n\n### Installation\n\n#### Using SBT\n\nAdd the following to your `build.sbt`:\n\n```scala\nresolvers += \"jitpack\" at \"https://jitpack.io\"\n\nlibraryDependencies += \"com.github.suprnation.cats-actors\" %% \"cats-actors\" % \"2.0.1\"\n```\n\n#### Using Maven\n\nAdd the following to your `pom.xml`:\n\n```xml\n\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003ejitpack.io\u003c/id\u003e\n        \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.suprnation.cats-actors\u003c/groupId\u003e\n    \u003cartifactId\u003ecats-actors_2.13\u003c/artifactId\u003e\n    or\n    \u003cartifactId\u003ecats-actors_3\u003c/artifactId\u003e\n    \u003cversion\u003e2.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n#### Using Bazel\n\nAdd the following to your `repositories.bzl` and `WORKSPACE` files:\n\n`repositories.bzl`\n\n```python\ndef load_dependencies():\n    maven_install(\n        artifacts = [\n            \"com.github.suprnation.cats-actors:cats-actors_2_13:2.0.1\", // or\n            \"com.github.suprnation.cats-actors:cats-actors_3:2.0.1\",\n        ],\n        repositories = [\n            \"https://jitpack.io\",\n        ],\n    )\n```\n\n`WORKSPACE`\n\n```\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nRULES_JVM_EXTERNAL_TAG = \"4.3\"\nRULES_JVM_EXTERNAL_SHA = \"eac6aeece657c8be3b9bb0a8f25a6ab5daedc2a0736f64c19d5cf1e6a3a5ba4a\"\n\nhttp_archive(\n    name = \"rules_jvm_external\",\n    urls = [\"https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip\" % RULES_JVM_EXTERNAL_TAG],\n    sha256 = RULES_JVM_EXTERNAL_SHA,\n)\n\nload(\"@rules_jvm_external//:defs.bzl\", \"maven_install\")\n\nload(\"//:repositories.bzl\", \"load_dependencies\")\n\nload_dependencies()\n```\n\nand update the `BUILD` as follows\n\n```\njava_library(\n    name = \"my_library\",\n    srcs = glob([\"src/main/java/**/*.java\"]),\n    deps = [\n        \"@maven//:com_github_suprnation_cats_actors\",\n    ],\n)\n```\n\nThis should help integrate `cats-actors` into your projects using SBT, Maven, or Bazel.\n\n## Basic Usage\n\n### Hello World Example with Cats-Actors (Tell syntax)\n\nCats-Actors leverages Cats Effect for managing effects and supports a generic `F[_]` type, allowing interoperability with other effect types.\n\nHere's a simple example of an actor system with a \"Hello World\" actor using Cats Effect:\n\nFirst, define an actor that will handle the \"Hello World\" message.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nsealed trait Request\ncase object Hello extends Request\n\ncase class HelloWorldActor() extends Actor[IO, Request] {\n  override def receive: Receive[IO, Request] = { case Hello =\u003e\n    IO.println(\"Hello, World!\")\n  }\n}\n```\n\nNext, create an actor system to manage the actor and send a message to it.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nobject HelloWorldApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"HelloWorldSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.actorOf(HelloWorldActor(), \"helloWorldActor\")\n        _ \u003c- actorRef ! Hello\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. Define the Actor:\n\n- `HelloWorldActor extends Actor[IO, Request]`, meaning it processes `Request` messages within the `IO` effect. This ensures that all messages the actor can receive will be of type `Request`.\n- The `receive` method is overridden to handle incoming messages. Here, it simply prints \"Hello, World!\" when it receives the `Hello` message.\n\n2. Create the Actor System and Send a Message:\n\n- `ActorSystem[IO](\"HelloWorldSystem\")` creates a new actor system named \"HelloWorldSystem\".\n- `actorSystemResource.use { system =\u003e ... }` uses the `.use` method to manage the lifecycle of the actor system.\n- `system.actorOf(HelloWorldActor(), \"helloWorldActor\")` creates an instance of `HelloWorldActor` within the actor system.\n- `actorRef ! Hello` sends the `Hello` message to the actor.\n- `system.waitForTermination` waits for the actor system to terminate. This ensures that all actors have completed their work before the application exits.\n- The actor system is automatically cleaned up after the `use` block completes.\n\n### Type Safety\n\nSince the actor is typed with `Request`, only messages of type `Request` can be sent to it. For example, the following code would not be allowed:\n\n```scala\n_ \u003c- actorRef ! \"Hello\"  // Error: Type mismatch\n_ \u003c- actorRef ! 123      // Error: Type mismatch\n```\n\n#### What does `waitForTermination` do?\nThe `waitForTermination` method is used to keep the actor system alive and waiting for messages unless the actor system\nitself dies internally, for example, due to supervision escalation. This is useful in scenarios where you want to ensure\nthat the actor system remains active and responsive to incoming messages until it is explicitly terminated or an\ninternal failure occurs.\n\nIn the context of the Hello World example, `waitForTermination` ensures that the actor system remains active. But what if we do not want to wait forever? what if we want to just ensure that the actor has received the message and just processed that message? There are various ways how one can achieve this but one way is to use the `ask` syntax described\nbelow.\n\n### Hello World Example with Cats-Actors (Ask syntax)\n\nBelow is a comprehensive example of a Hello World application using Cats-Actors using the `?` syntax for querying an actor.\n\nFirst, let's define an actor that will handle the `Hello` message and reply with `World` .\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.ReplyingReceive\nimport com.suprnation.actor.ReplyingActor\n\nsealed trait Request\ncase object Hello extends Request\n\nsealed trait Response\ncase object World extends Response\n\nclass HelloWorldActor extends ReplyingActor[IO, Request, Response] {\n  override def receive: ReplyingReceive[IO, Request, Response] = { case Hello =\u003e\n    IO.println(\"Hello, World!\").as(World)\n  }\n}\n```\n\nNote that in this case since we want to ensure that the request is of type `Request` and the reply from an actor is of type `Response` we extend from `ReplyingActor[IO, Request, Response]` which forces the actor to compute an `F[Response]` for every message which will be redirected to the sender.\n\nNext, create an actor system to manage the actor and query it using the `?` syntax.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nobject HelloWorldApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"HelloWorldSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.replyingActorOf(new HelloWorldActor, \"helloWorldActor\")\n        response \u003c- actorRef ? Hello\n        _ \u003c- IO.println(s\"Received response: $response\")\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. Define the Actor:\n\n- `HelloWorldActor extends ReplyingActor[IO, Request, Response]`, meaning it processes `Request` messages and replies with `Response` within the `IO` effect.\n- The `receive` method is overridden to handle the `Hello` messages (of type `Request`). Here, it prints \"Hello, World!\" and returns `World` (of type `Response`).\n\n2. Create the Actor System and Query the Actor:\n\n- `ActorSystem[IO](\"HelloWorldSystem\")` creates a new actor system named \"HelloWorldSystem\".\n- `actorSystemResource.use { system =\u003e ... }` uses the `.use` method to manage the lifecycle of the actor system.\n- `system.replyingActorOf(new HelloWorldActor, \"helloWorldActor\")` creates an instance of `HelloWorldActor` within the actor system.\n- `actorRef ? Hello` sends the `Hello` message to the actor and waits for a response. Note that the `ask` syntax will return a `Response`, the declared return type of the `ReplyingActor[IO, Request, Response]`.\n- `IO.println(s\"Received response: $response\")` prints the response received from the actor.\n- The actor system is automatically cleaned up after the `use` block completes.\n\n\u003e **Note:** that if you do not care about the reply but only care that the actor has processed the message you can use the `?!` syntax. We can rewrite the above example as:\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nobject HelloWorldApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"HelloWorldSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.replyingActorOf(new HelloWorldActor, \"helloWorldActor\")\n        _ \u003c- actorRef ?! \"hello\"\n        _ \u003c- IO.println(s\"Actor all done!\")\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n#### Why `waitForTermination` is **not** Required\n\nIn this example, `waitForTermination` is not required because the actor system will automatically wait for a reply from\nthe actor when using the `?` syntax. The `?` syntax sends a message to the actor and waits for a response of type `Response`, ensuring that the\nactor system remains active until the response is received. This makes `waitForTermination` unnecessary in scenarios where\nyou are querying an actor and expecting a reply.\n\nBy using the `?` syntax, the actor system handles the lifecycle and ensures that the application waits for the actor's\nresponse before proceeding. This simplifies the code and avoids the need for explicit termination handling.\n\n## Sequential vs Parallel send\n\n### Using cats-effects to send sequential messages to an actor\n\nBelow is a comprehensive example of an actor that receives multiple messages forever. We will use Cats-Effect to leverage fibers for concurrent message sending in the background.\n\nFirst, define an actor that will handle multiple messages.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\ncase class EchoActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case message =\u003e\n    IO.println(s\"Received message: $message\")\n  }\n}\n```\n\nNext, create an actor system to manage the actor and send messages to it in a background fiber.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport cats.implicits._\nimport com.suprnation.actor.ActorRef.ActorRef\nimport com.suprnation.actor.ActorSystem\n\nimport scala.concurrent.duration._\n\nobject EchoApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"EchoSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.actorOf(EchoActor(), \"echoActor\")\n        _ \u003c- sendMessagesForever(actorRef).start // Start sending messages in a background fiber\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n\n  def sendMessagesForever(actorRef: ActorRef[IO, String]): IO[Unit] = {\n    val messages = List(\"Hello\", \"World\", \"This\", \"Is\", \"Cats-Actors\")\n    val sendMessage = messages.traverse_(msg =\u003e (actorRef ! msg) \u003e\u003e IO.sleep(1.second))\n    sendMessage.foreverM\n  }\n}\n```\n\n### Explanation\n\n1. Define the Actor:\n\n- `EchoActor extends Actor[IO, String]`, meaning it processes `String` messages within the `IO` effect.\n- The `receive` method is overridden to handle incoming messages. Here, it prints each received message.\n\n2. Create the Actor System and Send Messages in a Fiber:\n\n- `ActorSystem[IO](\"EchoSystem\")` creates a new actor system named \"EchoSystem\".\n- `actorSystemResource.use { system =\u003e ... }` uses the `.use` method to manage the lifecycle of the actor system.\n- `system.actorOf(EchoActor(), \"echoActor\")` creates an instance of `EchoActor` within the actor system.\n- `sendMessagesForever(actorRef).start` starts sending messages to the actor in a background fiber.\n- The actor system waits until an internal error happens, in such an eventuality the actor system is cleaned up after the `use` block completes.\n\n3. Send Messages Forever:\n\n- `sendMessagesForever` is a function that sends a list of messages to the actor repeatedly.\n- `messages.traverse_(msg =\u003e actorRef ! msg \u003e\u003e IO.sleep(1.second))` sends each message to the actor with a 1-second delay between messages.\n- `sendMessage.foreverM` repeats the message sending process indefinitely.\n\n#### Leveraging Cats-Effect\nBy using Cats-Effect, we can leverage its powerful concurrency primitives to create complex use cases like this one. The `start` method allows us to run the message-sending process in a background fiber, ensuring that the actor continues to receive messages indefinitely without blocking the main application flow. The `foreverM` method is used to repeat the message-sending process forever, demonstrating how Cats-Effect can be used to build highly concurrent and resilient applications.\n\n### Using cats-effects to send parallel messages to an actor\n\nBelow is a comprehensive example of an actor that receives multiple messages in parallel. We will use Cats-Effect to leverage fibers for concurrent message sending.\n\nFirst, define an actor that will handle multiple messages.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\ncase class EchoActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case message =\u003e\n    IO.println(s\"Received message: $message\")\n  }\n}\n```\n\nNext, create an actor system to manage the actor and send messages to it in parallel.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport cats.syntax.all._\nimport com.suprnation.actor.ActorRef.ActorRef\nimport com.suprnation.actor.ActorSystem\n\nobject EchoApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"EchoSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.actorOf(EchoActor(), \"echoActor\")\n        _ \u003c- sendMessagesInParallel(actorRef) // Start sending messages in parallel\n        _ \u003c- system.waitForTermination // Keep the actor system alive\n      } yield ExitCode.Success\n    }\n  }\n\n  def sendMessagesInParallel(actorRef: ActorRef[IO, String]): IO[Unit] = {\n    val messages = List(\"Hello\", \"World\", \"This\", \"Is\", \"Cats-Actors\")\n    val sendMessage = messages.parTraverse_(msg =\u003e actorRef ! msg)\n    sendMessage\n  }\n}\n```\n\n### Explanation\n\n1. Define the Actor:\n\n- `EchoActor extends Actor[IO, String]`, meaning it processes `String` messages within the `IO` effect.\n- The `receive` method is overridden to handle incoming messages. Here, it prints each received message.\n\n2. Create the Actor System and Send Messages in Parallel:\n\n- `ActorSystem[IO](\"EchoSystem\")` creates a new actor system named \"EchoSystem\".\n- `actorSystemResource.use { system =\u003e ... }` uses the `.use` method to manage the lifecycle of the actor system.\n- `system.actorOf(EchoActor(), \"echoActor\")` creates an instance of `EchoActor` within the actor system.\n- `sendMessagesInParallel(actorRef)` sends messages to the actor in parallel.\n- `_ \u003c- system.waitForTermination` keeps the actor system alive and waiting for messages unless the actor system itself dies internally.\n- The actor system is automatically cleaned up after the use block completes.\n\n3. Send Messages in Parallel:\n\n- `sendMessagesInParallel` is a function that sends a list of messages to the actor in parallel.\n- `messages.parTraverse_(msg =\u003e actorRef ! msg)` sends each message to the actor concurrently using `parTraverse_` from Cats, which runs the effects in parallel.\n\n\n#### Leveraging Cats-Effect for Concurrency\n\nBy using Cats-Effect, we can leverage its powerful concurrency primitives to create complex use cases like this one. The `parTraverse_` method allows us to run the message-sending process in parallel, ensuring that the actor receives messages concurrently.\n\n## Creating Actors\nBelow are three examples demonstrating how to create multiple actors in different ways using the `cats-actors` library.\n\n### Example 1: Creating Several Actors from the System Actor\n\nIn this example, we create multiple actors directly from the actor system.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport cats.implicits._\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorSystem\n\ncase class EchoActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case message =\u003e\n    IO.println(s\"Received message: $message\")\n  }\n}\n\nobject SystemActorApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"SystemActorApp\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef1 \u003c- system.actorOf(EchoActor(), \"echoActor1\")\n        actorRef2 \u003c- system.actorOf(EchoActor(), \"echoActor2\")\n        actorRef3 \u003c- system.actorOf(EchoActor(), \"echoActor3\")\n        _ \u003c- List(actorRef1, actorRef2, actorRef3).parTraverse_(_ ! \"Hello from SystemActorApp\")\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Example 2: Creating Actors from Within an Actor\nIn this example, we create multiple actors from within another actor.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport cats.implicits._\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorSystem\n\ncase class EchoActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case message =\u003e\n    IO.println(s\"Received message: $message\")\n  }\n}\n\ncase class ParentActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case \"createChildren\" =\u003e\n    for {\n      child1 \u003c- context.actorOf(EchoActor(), \"childActor1\")\n      child2 \u003c- context.actorOf(EchoActor(), \"childActor2\")\n      _ \u003c- List(child1, child2).parTraverse_(_ ! \"Hello from ParentActor\")\n    } yield ()\n  }\n}\n\nobject ParentActorApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"ParentActorApp\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        parentActor \u003c- system.actorOf(ParentActor(), \"parentActor\")\n        _ \u003c- parentActor ! \"createChildren\"\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Example 3: Creating an Actor from Within an Actor Created by Another Actor\n\nIn this example, we create an actor from within an actor that was itself created by another actor.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorSystem\n\ncase class EchoActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case message =\u003e\n    IO.println(s\"Received message: $message\")\n  }\n}\n\ncase class GrandParentActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case \"createParent\" =\u003e\n    for {\n      parent \u003c- context.actorOf(ParentActor(), \"parentActor\")\n      _ \u003c- parent ! \"createChild\"\n    } yield ()\n  }\n}\n\ncase class ParentActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case \"createChild\" =\u003e\n    for {\n      child \u003c- context.actorOf(EchoActor(), \"childActor\")\n      _ \u003c- child ! \"Hello from ParentActor\"\n    } yield ()\n  }\n}\n\nobject GrandParentActorApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"GrandParentActorApp\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        grandParentActor \u003c- system.actorOf(GrandParentActor(), \"grandParentActor\")\n        _ \u003c- grandParentActor ! \"createParent\"\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **Example 1:**\n\n- Creates three `EchoActor` instances directly from the actor system.\n- Sends a message to each actor in parallel.\n- _Note: The actor system is the parent of all created actors and is responsible for their supervision._\n\n2. **Example 2:**\n\n- Defines a `ParentActor` that creates two `EchoActor` instances when it receives the \"createChildren\" message.\n- Sends a message to each child actor in parallel.\n- _Note: The `ParentActor` is the parent of the created child actors and is responsible for their supervision._\n\n3. **Example 3:**\n\n- Defines a `GrandParentActor` that creates a `ParentActor` when it receives the \"createParent\" message.\n- The `ParentActor` creates an `EchoActor` when it receives the \"createChild\" message.\n- Sends a message to the child actor.\n- _Note: The `GrandParentActor` is the parent of the `ParentActor`, and the `ParentActor` is the parent of the `EchoActor`. Each parent actor is responsible for the supervision of its child actors._\n\n## Killing Actors\nIn actor systems, there are various ways to manage actor termination:\n\n- **PoisonPill**\n\n  - **Purpose**: Gracefully stops an actor.\n  - **Behavior**: When an actor receives a PoisonPill, it processes all messages in its mailbox before stopping.\n  - **Use Case**: Preferred for a clean shutdown where the actor can finish its pending work.\n- **Kill**\n\n  - **Purpose**: Forcibly stops an actor.\n  - **Behavior**: When an actor receives a Kill message, it throws an exception (typically an ActorKilledException), causing the actor to immediately terminate and restart if supervised.\n  - **Use Case**: Useful when you need to abruptly stop an actor, but it can disrupt ongoing processes.\n- **context.self.stop**\n\n  - **Purpose**: Directly stops the actor from within its own context.\n  - **Behavior**: Immediately stops the actor. Any messages in the mailbox are discarded, and the actor transitions to the stopped state.\n  - **Use Case**: Suitable for scenarios where the actor decides to terminate itself after completing its work or encountering a condition that requires it to stop.\n\nLet's illustrate these with examples\n\n### Example 1: Killing an Actor Using context.self.stop\n\nIn this example, we define an actor that stops itself when it receives a specific message - `Stop`.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorSystem\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\n\ntrait Request\ncase object Stop extends Request\ncase class Message(msg: String) extends Request\n\ncase class SelfStoppingActor() extends Actor[IO, Request] {\n  override def receive: Receive[IO, Request] = {\n    case Stop =\u003e\n      IO.println(\"Stopping actor...\") \u003e\u003e context.self.stop\n    case Message(msg) =\u003e\n      IO.println(s\"Received message: $msg\")\n  }\n}\n\nobject SelfStoppingActorApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"SelfStoppingActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.actorOf(SelfStoppingActor(), \"selfStoppingActor\")\n        _ \u003c- actorRef ! Message(\"Hello, Actor!\")\n        _ \u003c- actorRef ! Stop\n        _ \u003c- IO.sleep(1.second) // Give some time for the actor to stop\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Example 2: Killing an Actor Using PoisonPill\n\nIn this example, we define an actor that stops when it receives a PoisonPill message.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.{ActorSystem, PoisonPill}\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\n\ntrait Request\ncase class Message(message: String) extends Request\n\ncase class PoisonPillActor() extends Actor[IO, Request] {\n  override def receive: Receive[IO, Request] = { case Message(msg) =\u003e\n    IO.println(s\"Received message: $msg\")\n  }\n}\n\nobject PoisonPillActorApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"PoisonPillActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.actorOf(PoisonPillActor(), \"poisonPillActor\")\n        _ \u003c- actorRef ! Message(\"Hello, Actor!\")\n        _ \u003c- actorRef !* PoisonPill // Note the use of !*\n        _ \u003c- IO.sleep(1.second) // Give some time for the actor to stop\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Example 3: Killing an Actor Using Kill\n\nIn this example, we define an actor that stops when it receives a Kill message.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.{ActorSystem, Kill}\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\n\ntrait Request\ncase class Message(message: String) extends Request\n\ncase class KillableActor() extends Actor[IO, Request] {\n  override def receive: Receive[IO, Request] = { case Message(msg) =\u003e\n    IO.println(s\"Received message: $msg\")\n  }\n}\n\nobject KillableActorApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] = ActorSystem[IO](\"KillableActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        actorRef \u003c- system.actorOf(KillableActor(), \"killableActor\")\n        _ \u003c- actorRef ! Message(\"Hello, Actor!\")\n        _ \u003c- actorRef !* Kill // Note the use of !*\n        _ \u003c- IO.sleep(1.second) // Give some time for the actor to stop\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **Example 1: Killing an Actor Using `context.self.stop`**:\n\n- `SelfStoppingActor`:\n  - Stops itself when it receives the `Stop` message (automatically handled by the Actor).\n  - Prints a message when it receives any other message.\n- `SelfStoppingActorApp`:\n  - Creates an actor system named \"SelfStoppingActorSystem\".\n  - Creates an actor of type `SelfStoppingActor`.\n  - Sends a message to the actor and then sends the `Stop` message to stop the actor.\n\n2. **Example 2: Killing an Actor Using `PoisonPill`**:\n\n- `PoisonPillActor`:\n  - Stops itself when it receives a `PoisonPill` message (automatically handled by the Actor).\n  - Prints a message when it receives any other message.\n- `PoisonPillActorApp`:\n  - Creates an actor system named \"PoisonPillActorSystem\".\n  - Creates an actor of type `PoisonPillActor`.\n  - Sends a message to the actor and then sends the `PoisonPill` message to stop the actor.\n\n3. **Example 3: Killing an Actor Using `Kill`**:\n\n- `KillableActor` stops itself when it receives a `Kill` message.\n- `KillableActorApp` creates an actor system, sends a message to the actor, and then sends the `Kill` message to stop the actor.\n\n### `!*` Method\n\nThe `!*` method is used to send system commands to actors. The type definition of `!*` is:\n\n```scala\n  def !*(message: =\u003e SystemCommand)(implicit\n      sender: Option[ActorRef[F, Nothing]] = None\n  ): F[Unit]\n```\n\nThere are two system commands, `Kill` and `PoisonPill`, which can be sent using this method.\n\nThese examples demonstrate how to kill actors using `context.self.stop`, `PoisonPill`, and `Kill` in the `cats-actors` library.\n\n## Passing Actor Addresses (ActorRef) around\nActors communicate by sending messages. To communicate with each other, they need to know each other's addresses. Actors can pass addresses in two ways:\n- During actor creation\n- Passed as a message\n\nLet's explore these approaches.\n\n### Passing Actor Address during creation\n\nIn this section, we will demonstrate how to pass an actor's address during its creation. This approach allows an actor to communicate with another actor by having its reference from the moment it is created. This can be useful when setting up structured communication patterns between actors right from the start.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.ActorRef\n\ncase class Question(text: String)\n\ncase class RespondingActor(replyTo: ActorRef[IO, Question]) extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case question =\u003e\n    replyTo ! Question(question)\n  }\n}\n```\n\nWhen the `RespondingActor` is constructed, we pass in an `ActorRef[IO, Question]`, which is an address of an actor that accepts `Question` messages. When the actor receives a message (which must be of type `String`), it converts the message to a `Question` type and sends it to the `replyTo` actor.\n\nWhat if we wanted to receive a reply from this actor with the answer to our question? Can we guarantee that the queries will be answered? Yes! Let's see how to achieve this.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ReplyingActorRef\n\ncase class Question(text: String)\ncase class Answer(text: String)\n\ncase class RespondingActor(replyTo: ReplyingActorRef[IO, Question, Answer]) extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case question =\u003e\n    for {\n      answer \u003c- replyTo ? Question(question)\n    } yield answer.text\n  }\n}\n```\n\nHere, we take in a message of type `String` and convert it to a `Question`. However, this time we use `ReplyingActorRef[IO, Question, Answer]` to guarantee that any replies we receive from the `replyTo` address are of type `Answer`.\n\n### Passing Actor Address within a message\nAnother common technique in message-passing systems is to include the reply address within the message itself. This approach allows the sender to specify where the response should be sent, providing flexibility for dynamic interactions between actors. By embedding the recipient's address in the message, actors can engage in more complex communication patterns without needing to know each other's addresses in advance.\n\n\n```scala\nimport cats.effect.IO\nimport cats.implicits._\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.ActorRef\n\ncase class Question(text: String, replyTo: ActorRef[IO, Answer])\ncase class Answer(text: String)\n\ncase class RespondingActor() extends Actor[IO, Question] {\n  override def receive: Receive[IO, Question] = { case Question(question, replyTo) =\u003e\n    for {\n      answer \u003c- Answer(\"complex computation to get the answer...\").pure[IO]\n      _ \u003c- replyTo ! answer\n    } yield answer.text\n  }\n}\n```\n\nIn the receive method, we get a `Question` which contains the question itself and the reply address. The reply address must understand `Answer` messages.\n\n\u003e Note that cats-actors still support `context.sender` and `context.parent`; however, these should be avoided since they are not typed and will require type coercion to send messages to these addresses.\n\n## Context sender and parent\nIn actor systems, understanding the `context` in which actors operate is crucial for managing communication and\nsupervision. Two important elements of this `context` are `sender` and `parent`.\n\n### context.sender\n\nRepresents the actor that sent the current message. Allows the receiving actor to respond directly to the sender.\n\n````scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nclass RespondingActor extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case msg =\u003e\n    context.sender.get ! s\"Received your message: $msg\"\n  }\n}\n````\n\nHowever note that `context.sender` does not allow us to send messages, we have to force the type to `String` using `unsafeUpcastRequest[String]`` the type that we know the actor supports (we know this implicitly not explicitly).\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nclass RespondingActor extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case msg =\u003e\n    context.sender.get.unsafeUpcastRequest[String] ! s\"Received your message: $msg\"\n  }\n}\n```\n\n\u003e In cats-actors, `context.sender` is considered unsafe. Instead, the `ActorRef` should be passed directly in the message or as a closure during the construction of the actor. See the sections above for more information on these techniques.\n\n### context.parent\n\nRepresents the parent actor of the current actor. Useful for supervision and hierarchical message passing.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nclass ChildActor extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case msg =\u003e\n    context.parent ! s\"Child received: $msg\"\n  }\n}\n```\n\nNote that `context.parent`does not allow us to send messages to the parent since the system is not sure of the type.  We need to coerce the type once again using\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nclass ChildActor extends Actor[IO, String] {\n  override def receive: Receive[IO,String] = {\n    case msg =\u003e\n      (context.parent.unsafeUpcastRequest[String] ! s\"Child received: $msg\")\n  }\n}\n```\n\n### Example: Using context.sender and context.parent\nFirst, let's define the actors that will be used in the example.\n\n```scala\nimport cats.data.OptionT\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.ActorRef\n\ntrait Request\ncase class Message(msg: String) extends Request\ncase object GetParent extends Request\ncase object GetSender extends Request\n\ncase class ChildActor() extends Actor[IO, Request] {\n  override def receive: Receive[IO, Request] = {\n    case Message(msg) =\u003e\n      IO.println(s\"ChildActor received message: $msg from ${context.sender}\")\n    case GetParent =\u003e\n      IO.println(s\"ChildActor's parent: ${context.parent}\")\n    case GetSender =\u003e\n      IO.println(s\"ChildActor's sender: ${context.sender}\")\n  }\n}\n\ncase class ParentActor(childRef: Ref[IO, Option[ActorRef[IO, Request]]]) extends Actor[IO, Request] {\n\n  override def preStart: IO[Unit] =\n    for {\n      childActor \u003c- context.actorOf(ChildActor(), \"child-actor\")\n      _ \u003c- childRef.set(Some(childActor))\n    } yield ()\n\n  override def receive: Receive[IO, Request] = {\n    case Message(msg) =\u003e\n      IO.println(s\"ParentActor received message: $msg from ${context.sender}\") \u003e\u003e\n        OptionT(childRef.get).foreachF(_ ! Message(msg))\n    case GetParent =\u003e\n      IO.println(s\"ParentActor's parent: ${context.parent}\") \u003e\u003e\n        OptionT(childRef.get).foreachF(_ ! GetParent)\n    case GetSender =\u003e\n      IO.println(s\"ParentActor's sender: ${context.sender}\") \u003e\u003e\n        OptionT(childRef.get).foreachF(_ ! GetSender)\n  }\n}\n```\n\nNext, let's create the main application to demonstrate the usage.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp}\nimport com.suprnation.actor.ActorSystem\nimport com.suprnation.typelevel.actors.syntax._\n\nobject ActorContextExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource = ActorSystem[IO](\"ActorContextExampleSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        parentActor \u003c- system.actorOf(\n          Ref.of[IO, Option[ActorRef[IO, Request]]](None).map(ParentActor),\n          \"parent-actor\"\n        )\n\n        // Send a message to the parent and consequently the child actor\n        _ \u003c- parentActor ! Message(\"Hello from main\")\n        _ \u003c- system.waitForIdle()\n\n        // Print the parent of the parent and consequently the child actor\n        _ \u003c- parentActor ! GetParent\n        _ \u003c- system.waitForIdle()\n\n        // Print the sender in the parent and consequently the child actor\n        _ \u003c- parentActor ! GetSender\n        _ \u003c- system.waitForIdle()\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **Actor Definitions:**\n\n- `ChildActor`: Receives messages and prints the message along with the sender (`context.sender`). It also handles `GetParent` and `GetSender` messages to print the parent actor and the sender, respectively.\n- `ParentActor`: Receives messages and prints the message along with the sender (`context.sender`). It forwards messages to the `ChildActor` and handles `GetParent` and `GetSender` messages to print the parent actor and the sender, respectively.\n\n2. **Main Application:**\n\n- The `ActorContextExampleApp` creates an actor system and defines a `ParentActor` and a `ChildActor`.\n- It sends a `Message` to the `ParentActor`, which forwards it to the `ChildActor`.\n- It sends `GetParent` and `GetSender` messages to the `ChildActor` to demonstrate the usage of `context.parent` and `context.sender`.\n\n\u003e **Note:** When the main app sends a message, the sender is `None`. This is normal as there is no `ActorRef` from the main app.\n\n## Forwarding of Messages in Actor Systems\n\nForwarding messages in an actor system means sending a message from one actor to another on behalf of the original sender. This is useful when an actor needs to delegate tasks to other actors without altering the sender information, maintaining the original sender's identity for responses or acknowledgments.\n\n### Example: Forwarding Messages in an Actor System\n\nMessages can be forwarded using the `.forward` method or using the `\u003e\u003e!` syntax.\n\n#### Using .forward Method\n\nLet's create an example using the `.forward` method to forward messages.\n\n```scala\nimport cats.effect.{IO, Ref}\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.ActorRef\n\ncase class ForwardMessage(msg: String)\n\ncase class ForwardActor(forwardTo: ActorRef[IO, ForwardMessage]) extends Actor[IO, ForwardMessage] {\n  override def receive: Receive[IO, ForwardMessage] = { case ForwardMessage(msg) =\u003e\n    forwardTo.forward(ForwardMessage(msg))\n  }\n}\n\ncase class BaseActor() extends Actor[IO, ForwardMessage] {\n  override def receive: Receive[IO, ForwardMessage] = { case ForwardMessage(msg) =\u003e\n    IO.println(s\"BaseActor received message: $msg from ${context.sender}\")\n  }\n}\n```\n\nNext, let's create the main application to demonstrate the usage.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Ref}\nimport com.suprnation.actor.ActorSystem\nimport com.suprnation.typelevel.actors.syntax._\n\nobject ForwardExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource = ActorSystem[IO](\"ForwardExampleSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        baseActor \u003c- system.actorOf(BaseActor(), \"base-actor\")\n        forwardActor \u003c- system.actorOf(ForwardActor(baseActor), \"forward-actor\")\n\n        // Send a message to the forward actor, which will forward it to the base actor\n        _ \u003c- forwardActor ! ForwardMessage(\"Hello using .forward\")\n        _ \u003c- system.waitForIdle()\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n#### Using \u003e\u003e! Syntax\n\nNow, let's create an example using the `\u003e\u003e!` syntax to forward messages.\n\n```scala\nimport cats.effect.{IO, Ref}\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.ActorRef\n\ncase class ForwardMessage(msg: String)\n\ncase class ForwardActor(forwardTo: ActorRef[IO, ForwardMessage]) extends Actor[IO, ForwardMessage] {\n  override def receive: Receive[IO, ForwardMessage] = { case ForwardMessage(msg) =\u003e\n    forwardTo \u003e\u003e! ForwardMessage(msg)\n  }\n}\n\ncase class BaseActor() extends Actor[IO, ForwardMessage] {\n  override def receive: Receive[IO, ForwardMessage] = { case ForwardMessage(msg) =\u003e\n    IO.println(s\"BaseActor received message: $msg from ${context.sender}\")\n  }\n}\n```\n\n#### Explanation\n\n1. **Actor Definitions:**\n\n- **ForwardActor**: Forwards messages to another actor (`forwardTo`) and sets the sender reference.\n- **BaseActor**: Receives forwarded messages and prints them to the console.\n\n2. **Using `.forward` Method:**\n\n- The `ForwardExampleApp` sends a message to `forwardActor`, which forwards it to `BaseActor` using the `.forward` method.\n\n3. **Using `\u003e\u003e!` Syntax:**\n\n- The `ForwardSyntaxExampleApp` sends a message to `forwardActor`, which forwards it to `BaseActor` using the `\u003e\u003e!` syntax.\n\n\u003e **Note:** When an actor forwards a message, `context.sender` remains the original sender, allowing the receiving actor to respond directly to the original sender if needed.\n\n## Supervision\nSupervision in an actor system refers to the mechanism by which parent actors manage the lifecycle and failures of their child actors. It ensures fault tolerance and robust error handling in the system.\n\n### Key Concepts\n\n- **Parent-Child Hierarchy**: Actors are organized in a hierarchical structure where parent actors supervise their child actors.\n- **Supervision Strategy**: Defines how to handle failures in child actors. Common strategies include:\n  - `Restart`: Restarts the failed actor.\n  - `Stop`: Stops the actor permanently.\n  - `Resume`: Continues the actor's processing without any changes.\n  - `Escalate`: Passes the failure up to the parent actor.\n- **Supervision Strategies**\n  - `Restart Strategy`: Commonly used to handle transient errors. The actor's state is re-initialized, and it can resume processing new messages.\n  - `Stop Strategy`: Used when an actor encounters an unrecoverable error or when it no longer needs to process messages.\n  - `Resume Strategy`: Allows the actor to continue processing messages without restarting, used for recoverable errors that don't require state re-initialization.\n  - `Escalate Strategy`: Passes the failure to the parent actor to decide the next course of action.\n\nBelow is an example demonstrating supervision in an actor system using the `cats-actors` library. This example covers the key concepts and the above-mentioned supervision strategies.\n\n### Example: Supervision in an Actor System\n\nFirst, define the parent and child actors with different supervision strategies.\n\n```scala\nimport cats.effect.{IO, Ref}\nimport cats.implicits._\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.ActorRef\nimport com.suprnation.actor.SupervisorStrategy._\nimport com.suprnation.actor.{OneForOneStrategy, SupervisionStrategy}\n\nimport scala.concurrent.duration.DurationInt\nimport scala.language.postfixOps\n\ntrait Request\ncase object Fail extends Request\ncase object Illegal extends Request\ncase class Message(msg: String) extends Request\n\ncase class ParentActor(ref: Ref[IO, List[ActorRef[IO, Request]]]) extends Actor[IO, Request] {\n  override def supervisorStrategy: SupervisionStrategy[IO] =\n    OneForOneStrategy[IO](maxNrOfRetries = 3, withinTimeRange = 1 minute) {\n      case _: IllegalArgumentException =\u003e Stop\n      case _: RuntimeException         =\u003e Restart\n      case _: Exception                =\u003e Escalate\n    }\n\n  override def preStart: IO[Unit] =\n    for {\n      childActor \u003c- context.actorOf(ChildActor(), \"child-actor\")\n      _ \u003c- ref.update(_ :+ childActor)\n    } yield ()\n\n  override def receive: Receive[IO, Request] = { case msg =\u003e\n    for {\n      children \u003c- ref.get\n      _ \u003c- children.traverse_(child =\u003e child ! msg)\n    } yield ()\n  }\n}\n\ncase class ChildActor() extends Actor[IO, Request] {\n  override def receive: Receive[IO, Request] = {\n    case Fail    =\u003e IO.raiseError(new RuntimeException(\"Child actor failed (restarting)!\"))\n    case Illegal =\u003e IO.raiseError(new IllegalArgumentException(\"Illegal argument (stopping)!\"))\n    case msg     =\u003e IO.println(s\"Child actor received: $msg\")\n  }\n}\n```\n\nNext, create an actor system to manage the actors and send messages to demonstrate the supervision strategies.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Ref, Resource}\nimport com.suprnation.actor.ActorRef.ActorRef\nimport com.suprnation.actor.ActorSystem\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\n\nobject SupervisionExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"SupervisionExampleSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        ref \u003c- Ref[IO].of(List.empty[ActorRef[IO, Request]])\n        parentActor \u003c- system.actorOf(ParentActor(ref), \"parent-actor\")\n        _ \u003c- parentActor ! Message(\"Hello, Actor!\")\n        _ \u003c- parentActor ! Fail // This will cause the child actor to restart\n        _ \u003c- parentActor ! Message(\"Hello again!\")\n        _ \u003c- parentActor ! Illegal // This will cause the child actor to stop\n        _ \u003c- parentActor ! Message(\"Are you there?\")\n        _ \u003c- IO.sleep(1.second) // Give some time for the messages to be processed\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n**1. Define the Actors:**\n- `ParentActor`:\n  - Defines a `supervisorStrategy` using `OneForOneStrategy` with different handling for various exceptions.\n  - Creates a child actor in the `preStart` method and takes a reference (`ref`) which will hold the list of children actor addresses created.\n  - Forwards received messages to the child actor.\n- `ChildActor`:\n  - Raises different exceptions based on the received message to demonstrate the supervision strategies.\n  - Prints received messages.\n\n**2. Create the Actor System and Send Messages:**\n\n- `SupervisionExampleApp`:\n  - Creates an actor system named \"SupervisionExampleSystem\".\n  - Creates a `ParentActor` which in turn creates a `ChildActor`.\n  - Sends messages to the `ParentActor` to demonstrate the supervision strategies:\n    - Sends a normal message.\n    - Sends a \"fail\" message to cause the child actor to restart.\n    - Sends another normal message.\n    - Sends an \"illegal\" message to cause the child actor to stop.\n    - Sends another normal message to check if the child actor is still there. The actor will not be there, and the message is re-routed to the `dead-letter` queue.\n\n### Supervision Strategies Demonstrated\n\n- **Restart**: When the `ChildActor` receives the \"fail\" message, it raises a `RuntimeException`, causing the `ParentActor` to restart it.\n- **Stop**: When the `ChildActor` receives the \"illegal\" message, it raises an `IllegalArgumentException`, causing the `ParentActor` to stop it.\n- **Escalate**: Any other exceptions would be escalated to the parent actor, but this is not demonstrated in this example.\n\nThis example demonstrates how to implement and use supervision strategies in an actor system using the `cats-actors` library.\n\n## Actor Lifecycle Hooks\n\nActor lifecycle hooks provide methods to perform actions during different phases of an actor's lifecycle. These hooks\nallow for initialization, cleanup, and handling restarts.\n\n**Available Hooks**\n\n- `preStart`\n\n  - Called before the actor starts processing messages.\n  - Useful for initialization tasks.\n\n  ```scala\n     override def preStart(): IO[Unit] = IO(println(\"Actor is starting\"))\n  ```\n- `postStop`\n\n  - Called after the actor has been stopped.\n  - Useful for cleanup tasks.\n\n  ```scala\n  override def postStop(): IO[Unit] = IO(println(\"Actor has stopped\"))\n  ```\n- `preRestart`\n\n  - Called before the actor is restarted after a failure.\n  - Allows for cleanup before restarting.\n\n  ```scala\n  override def preRestart(reason: Throwable, message: Option[Any]): IO[Unit] =\n    IO(println(s\"Actor is restarting due to: ${reason.getMessage}\"))\n  ```\n- `postRestart`\n\n  - Called after the actor has been restarted.\n  - Used to reinitialize the actor.\n\n  ```scala\n  override def postRestart(reason: Throwable): IO[Unit] =\n    IO(println(\"Actor has been restarted\"))\n  ```\n- `preSuspend`\n\n  - This method is called before an actor is suspended, typically due to an error or some other condition that\n    requires the actor to be temporarily halted.\n  - This method takes two optional parameters:\n    - `reason`: An optional Throwable that indicates the reason for the suspension.\n    - `message`: An optional message that might have caused the suspension.\n\n  ```scala\n  override def preSuspend(reason: Option[Throwable], message: Option[Any]): IO[Unit] =\n      IO.println(s\"Actor is being suspended due to: ${reason.map(_.getMessage).getOrElse(\"unknown reason\")}\")\n      .flatMap(_ =\u003e reason.fold(IO.unit)(t =\u003e IO(t.printStackTrace())))\n  ```\n\n  \u003e **Note:** Only the actor which caused the error will receive the message. This is to ensure that no actor receives messages which are not intended for them\n  \u003e and thus result in information leak.\n\n\n### Example: Actor hooks\n\nLet's update the example to make use of the lifecycle hooks such as `preStart`, `postStop`, `preRestart`, `postRestart`, and `preSuspend`. These hooks allow you to define custom behavior at different stages of an actor's lifecycle.\n\n```scala\nimport cats.effect._\nimport cats.implicits._\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.ActorRef\nimport com.suprnation.actor.SupervisorStrategy._\nimport com.suprnation.actor.{ActorSystem, OneForOneStrategy, SupervisionStrategy}\n\nimport scala.concurrent.duration.DurationInt\nimport scala.language.postfixOps\n\ntrait Request\ncase object Fail extends Request\ncase object Illegal extends Request\ncase class Message(msg: String) extends Request\n\ncase class ParentActor(ref: Ref[IO, List[ActorRef[IO, Request]]]) extends Actor[IO, Request] {\n  override def supervisorStrategy: SupervisionStrategy[IO] =\n    OneForOneStrategy[IO](maxNrOfRetries = 3, withinTimeRange = 1 minute) {\n      case _: IllegalArgumentException =\u003e Stop\n      case _: RuntimeException         =\u003e Restart\n      case _: Exception                =\u003e Escalate\n    }\n\n  override def preStart: IO[Unit] =\n    for {\n      _ \u003c- IO.println(\"ParentActor is starting.\")\n      childActor \u003c- context.actorOf(ChildActor(), \"child-actor\")\n      _ \u003c- ref.update(_ :+ childActor)\n    } yield ()\n\n  override def postStop: IO[Unit] =\n    IO.println(\"ParentActor has stopped.\")\n\n  override def preRestart(reason: Option[Throwable], message: Option[Any]): IO[Unit] =\n    for {\n      _ \u003c- IO.println(\n        s\"ParentActor is restarting due to: ${reason.fold(\"No Reason\")(_.getMessage)}\"\n      )\n      _ \u003c- super.preRestart(reason, message)\n    } yield ()\n\n  override def postRestart(reason: Option[Throwable]): IO[Unit] =\n    IO.println(s\"ParentActor has restarted due to: ${reason.fold(\"No Reason\")(_.getMessage)}\")\n\n  override def preSuspend(reason: Option[Throwable], message: Option[Any]): IO[Unit] =\n    for {\n      _ \u003c- IO.println(\n        s\"ParentActor is being suspended due to: ${reason.map(_.getMessage).getOrElse(\"unknown reason\")}\"\n      )\n      _ \u003c- reason.fold(IO.unit)(t =\u003e IO(t.printStackTrace()))\n      _ \u003c- super.preSuspend(reason, message)\n    } yield ()\n\n  override def receive: Receive[IO, Request] = { case msg =\u003e\n    for {\n      children \u003c- ref.get\n      _ \u003c- children.traverse_(child =\u003e child ! msg)\n    } yield ()\n  }\n}\n\ncase class ChildActor() extends Actor[IO, Request] {\n  override def preStart: IO[Unit] =\n    IO.println(\"ChildActor is starting.\")\n\n  override def postStop: IO[Unit] =\n    IO.println(\"ChildActor has stopped.\")\n\n  override def preRestart(reason: Option[Throwable], message: Option[Any]): IO[Unit] =\n    for {\n      _ \u003c- IO.println(\n        s\"ChildActor is restarting due to: ${reason.map(_.getMessage).getOrElse(\"unknown reason\")}\"\n      )\n      _ \u003c- super.preRestart(reason, message)\n    } yield ()\n\n  override def postRestart(reason: Option[Throwable]): IO[Unit] =\n    for {\n      _ \u003c- IO.println(\n        s\"ChildActor has restarted due to: ${reason.map(_.getMessage).getOrElse(\"unknown reason\")}\"\n      )\n      _ \u003c- super.postRestart(reason)\n    } yield ()\n\n  override def preSuspend(reason: Option[Throwable], message: Option[Any]): IO[Unit] =\n    for {\n      _ \u003c- IO.println(\n        s\"ChildActor is being suspended due to: ${reason.map(_.getMessage).getOrElse(\"unknown reason\")}\"\n      )\n      _ \u003c- reason.fold(IO.unit)(t =\u003e IO(t.printStackTrace()))\n      _ \u003c- super.preSuspend(reason, message)\n    } yield ()\n\n  override def receive: Receive[IO, Request] = {\n    case Fail    =\u003e IO.raiseError(new RuntimeException(\"Child actor failed (restarting)!\"))\n    case Illegal =\u003e IO.raiseError(new IllegalArgumentException(\"Illegal argument (stopping)!\"))\n    case Message(msg) =\u003e IO.println(s\"Child actor received: $msg\")\n  }\n}\n\nobject SupervisionExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"SupervisionExampleSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        children \u003c- Ref[IO].of(List.empty[ActorRef[IO, Request]])\n        parentActor \u003c- system.actorOf(ParentActor(children), \"parent-actor\")\n        _ \u003c- parentActor ! Message(\"Hello, Actor!\")\n        _ \u003c- parentActor ! Fail // This will cause the child actor to restart\n        _ \u003c- parentActor ! Message(\"hello again!\")\n        _ \u003c- parentActor ! Illegal // This will cause the child actor to stop\n        _ \u003c- parentActor ! Message(\"Are you there?\")\n        _ \u003c- IO.sleep(1.second) // Give some time for the messages to be processed\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n#### Lifecycle Hooks\n\n1. `preStart`:\n\n- Called before the actor starts processing messages.\n- Used to initialize resources or perform setup tasks.\n- In `ChildActor`, it prints a message indicating that the actor is starting.\n\n2. `postStop`:\n\n- Called after the actor has stopped processing messages.\n- Used to clean up resources or perform teardown tasks.\n- In `ChildActor`, it prints a message indicating that the actor has stopped.\n\n3. `preRestart`:\n\n- Called before the actor is restarted due to a failure.\n- Used to perform tasks before the actor restarts, such as cleaning up resources.\n- In `ChildActor`, it prints a message indicating the reason for the restart and calls the superclass's `preRestart` method to ensure any default behavior is executed.\n\n4. `postRestart`:\n\n- Called after the actor has restarted due to a failure.\n- Used to perform tasks after the actor restarts, such as reinitializing resources.\n- In `ChildActor`, it prints a message indicating the reason for the restart and calls the superclass's `postRestart` method to ensure any default behavior is executed.\n\n5. `preSuspend`:\n\n- Called before the actor is suspended.\n- Used to perform tasks before the actor is suspended, such as logging the reason for suspension.\n- In `ChildActor`, it prints a message indicating the reason for suspension, prints the stack trace if a `Throwable` is provided, and calls the superclass's `preSuspend` method to ensure any default behavior is executed.\n\n#### `receive` Method\n\nThe `receive` method defines how the actor handles incoming messages. In `ChildActor`, it handles three types of messages:\n\n1. Fail:\n\n- Raises a `RuntimeException` with a message indicating that the child actor failed and will be restarted.\n\n2. Illegal:\n\n- Raises an `IllegalArgumentException` with a message indicating that an illegal argument was provided and the actor will be stopped.\n\n3. Message:\n\n- Prints the received message to the console.\n\n#### Output\n\nThe expected output from this run is as follows:\n\nOn startup of the system you should see:\n\n```bash\nParentActor is starting.\nChildActor is starting.\n```\n\nWhen the first message is received:\n\n```bash\nChild actor received: Hello, Actor! // First message received\n```\n\nWhen the \"fail\" message is received, the parent should restart the child actor:\n\n```bash\nChildActor is being suspended due to: Child actor failed (restarting)! // Received fail (preSuspend hook)\nChildActor is restarting due to: Child actor failed (restarting)! // Received fail (preRestart hook)\nChildActor has stopped. // Received fail (postStop hook)\nChildActor has restarted due to: Child actor failed (restarting)! // Received fail (postRestart hook)\nChildActor is starting. // Received fail (preStart hook)\n```\n\nWhen \"Hello again!\" is received:\n\n```bash\nChild actor received: Hello again! // 3rd message \"Hello again!\"\n```\n\nWhen \"illegal\" is received, the parent should terminate the child actor:\n\n```bash\nChildActor is being suspended due to: Illegal argument (stopping)! // Received illegal (preSuspend hook)\nChildActor has stopped. // Received illegal (postStop hook)\n```\n\nAny subsequent message (\"Are you there?\") will flow to the dead-letter queue:\n\n```bash\n[EventBus] =\u003e Debug([Path: localhost/dead-letter] DeadLetter(Envelope(Are you there?,Some([System: SupervisionExampleSystem] ... [name: child-actor]}))) // Message \"Are you there?\" flows to the dead-letter\n```\n\n===\n\n## Actor Watch\n\nActor watch is a mechanism in actor systems that allows one actor to monitor the lifecycle of another actor. When an\nactor watches another actor, it will be notified if the watched actor stops, either normally or due to failure.\n\n*Key Concepts*\n\n- **Watching**: An actor can start watching another actor using the watch method.\n- **Termination Notifications**: If the watched actor stops, the watcher actor receives a user defined termination message.\n- **Unwatching**: An actor can stop watching another actor using the unwatch method.\n\n### Example: Watching Actors\n\nLet's define two actors: `ParentActor` and `ChildActor`. The `ParentActor` will watch the `ChildActor` and handle its termination.\n\n`ParentActor` and `ChildActor` Definitions\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\nimport com.suprnation.actor.ActorRef.NoSendActorRef\n\ntrait Request\ncase object StopChild extends Request\ncase class Message(message: String) extends Request\ncase class ActorTerminated(actorRef: NoSendActorRef[IO]) extends Request\n\ncase class ParentActor() extends Actor[IO, Request] {\n  override def preStart: IO[Unit] =\n    for {\n      _ \u003c- IO.println(\"ParentActor is starting.\")\n      child \u003c- context.actorOf(ChildActor(), \"child-actor\")\n      _ \u003c- context.watch(child, ActorTerminated(child)) // Watch the child actor\n    } yield ()\n\n  override def receive: Receive[IO, Request] = {\n    case StopChild =\u003e\n      for {\n        children \u003c- context.children\n        _ \u003c- children.headOption.fold(IO.unit)(child =\u003e context.stop(child))\n      } yield ()\n    case ActorTerminated(ref) =\u003e\n      IO.println(s\"ParentActor received termination notification for: ${ref.path.name}\")\n  }\n}\n\ncase class ChildActor() extends Actor[IO, Request] {\n  override def preStart: IO[Unit] =\n    IO.println(\"ChildActor is starting.\")\n\n  override def postStop: IO[Unit] =\n    IO.println(\"ChildActor has stopped.\")\n\n  override def receive: Receive[IO, Request] = { case msg =\u003e\n    IO.println(s\"Child actor received: $msg\")\n  }\n}\n```\n\nNext, let's create an `IOApp` to run the example. The `ParentActor` will watch the `ChildActor`, and we will send a message to stop the `ChildActor` to see how the `ParentActor` handles the termination notification.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\n\nobject ActorWatchExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"ActorWatchExampleSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        parentActor \u003c- system.actorOf(ParentActor(), \"parent-actor\")\n        _ \u003c- parentActor ! StopChild // Send a message to stop the child actor\n        _ \u003c- IO.sleep(1.second) // Give some time for the messages to be processed\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. `ParentActor`:\n\n- In the `preStart` hook, it creates a `ChildActor` and starts watching it using the `watch` method.\n- In the `receive` method, it handles two types of messages:\n  - `StopChild`: Stops the child actor.\n  - `ActorTerminated(ref)`: Handles the termination notification for the watched actor and prints a message.\n\n2. `ChildActor`:\n- Prints a message when it starts and stops.\n- In the `receive` method, it prints any received message.\n\n3. `ActorWatchExampleApp`:\n- Creates an `ActorSystem` and a `ParentActor`.\n- Sends a message to the `ParentActor` to stop the `ChildActor` - `StopChild`.\n- Waits for a short period to allow the messages to be processed.\n\n\u003e **Note:** The `context.watch(child, ActorTerminated(child))` instruction tells the system to send the `ActorTerminated(actorRef)` message when the actor is terminated.\n\n#### Explanation of NoSendActorRef\n\n`NoSendActorRef[IO]` is an alias for `ReplyingActorRef[IO, Nothing, Any]`, representing an actor address to which we cannot send any messages (`Nothing` indicates no accepted message type), but can be used to reply with any type (`Any`), implying flexibility in responses. This design ensures that the reference can hold an actor's address while restricting its usage to non-sending operations, maintaining type safety and functional constraints within the actor system.\n\n## Scheduling messages\n\nLet's create an example that demonstrates how to use the `Scheduler` class and its `scheduleOnce` method to schedule a task to run after a specified delay.\n\n### Example: Using Scheduler.scheduleOnce\n\nIn this example, we'll create an actor that schedules a task to print a message to the console after a delay of 5 seconds.\n\n**Actor Definition**\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nimport scala.concurrent.duration._\n\ncase object ScheduleTask\n\ncase class SchedulerActor() extends Actor[IO, ScheduleTask.type] {\n  override def preStart: IO[Unit] =\n    // Schedule a task to run after a delay of 5 seconds\n    context.system.scheduler\n      .scheduleOnce(5.seconds) {\n        IO.println(\"Task executed after delay!\")\n      }\n      .void\n\n  override def receive: Receive[IO, ScheduleTask.type] = { case ScheduleTask =\u003e\n    IO.println(\"Received ScheduleTask message\")\n  }\n}\n```\n\nNext, let's create an `IOApp` to run the example. We'll create an `ActorSystem` and a `SchedulerActor`, and then send a message to observe the scheduling behavior.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\n\nobject SchedulerActorExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"SchedulerActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        schedulerActor \u003c- system.actorOf(SchedulerActor(), \"scheduler-actor\")\n        _ \u003c- schedulerActor ! ScheduleTask\n        // Wait for 6 seconds to ensure the scheduled task gets executed\n        _ \u003c- IO.sleep(6.seconds)\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **SchedulerActor**:\n\n- In the `preStart` method, `context.system.scheduler.scheduleOnce` is used to schedule a task to run after a delay of 5 seconds. The task prints a message to the console.\n- The `receive` method handles messages sent to the actor. In this example, it prints a message when it receives a `ScheduleTask` message.\n\n2. **SchedulerActorExampleApp**:\n\n- Creates an `ActorSystem` using a resource.\n- Creates a `SchedulerActor` and sends a `ScheduleTask` message to it.\n- Waits for 6 seconds to ensure that the scheduled task gets executed.\n\n## State Management - Context `become` / `unbecome`\n\nIn an actor system, `become` and `unbecome` are used to change an actor's behavior dynamically at runtime. This allows actors to handle different types of messages or states over time.\n\n**Become**\n\n- The `become` method changes the current behavior of the actor to a new behavior. This is useful for managing state transitions within an actor. The behavior is changed on the next message.\n\n**Unbecome**\n\n- The `unbecome` method reverts the actor's behavior to the previous one. This is typically used to handle state transitions that need to revert back to an original or default behavior. The behavior is changed on the next message.\n-\n\u003e **Note:** Whether `unbecome` will revert to the _previous_ or the _initial_ behavior depends on whether the `discardOld` parameter was specified when using `become`. By default `become` discards the previous behaviour except if it is the initial one, which is never discarded.\n\n### Example: Context `become` and `unbecome`\n\nIn this example, we'll create an actor that can switch between two behaviors: happy and angry. The actor will start in the happy state and can switch to the angry state when it receives a specific message. It can also switch back to the happy state.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nsealed trait MoodActorRequests\ncase object MakeAngry extends MoodActorRequests\ncase object MakeHappy extends MoodActorRequests\ncase object Greet extends MoodActorRequests\n\ncase class MoodActor() extends Actor[IO, MoodActorRequests] {\n  def happy: Receive[IO, MoodActorRequests] = {\n    case MakeAngry =\u003e\n      IO.println(\"Switching to angry state...\") \u003e\u003e context.become(angry)\n    case Greet =\u003e\n      IO.println(\"Hello! I'm happy!\")\n  }\n\n  def angry: Receive[IO, MoodActorRequests] = {\n    case MakeHappy =\u003e\n      IO.println(\"Switching to happy state...\") \u003e\u003e context.unbecome\n    case Greet =\u003e\n      IO.println(\"Go away! I'm angry!\")\n  }\n\n  override def receive: Receive[IO, MoodActorRequests] = happy\n}\n```\n\nNext, let's create an `IOApp` to run the example. We'll create an `ActorSystem` and a `MoodActor`, and then send messages to switch between states and observe the behavior.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nobject MoodActorExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"MoodActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        moodActor \u003c- system.actorOf(MoodActor(), \"mood-actor\")\n        _ \u003c- moodActor ! Greet // Should print: \"Hello! I'm happy!\"\n        _ \u003c- moodActor ! MakeAngry // Should switch to angry state\n        _ \u003c- moodActor ! Greet // Should print: \"Go away! I'm angry!\"\n        _ \u003c- moodActor ! MakeHappy // Should switch back to happy state\n        _ \u003c- moodActor ! Greet // Should print: \"Hello! I'm happy!\"\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **MoodActor**:\n\n- Defines two behaviors: `happy` and `angry`.\n- The `happy` behavior handles two messages:\n  - `MakeAngry`: Switches to the `angry` state using `context.become`.\n  - `Greet`: Prints a happy greeting.\n- The `angry` behavior handles two messages:\n  - `MakeHappy`: Switches back to the `happy` state using `context.unbecome`.\n  - `Greet`: Prints an angry greeting.\n- The initial behavior is set to `happy` in the `receive` method.\n\n2. **MoodActorExampleApp**:\n\n- Creates an `ActorSystem` and a `MoodActor`.\n- Sends a series of messages to the `MoodActor` to switch between states and observe the behavior:\n  - `Greet`: Prints the current greeting based on the state.\n  - `MakeAngry`: Switches to the angry state.\n  - `MakeHappy`: Switches back to the happy state.\n\nThis example demonstrates how to use `context.become` and `context.unbecome` to change an actor's behavior dynamically. The `MoodActor` switches between `happy` and `angry` states based on the received messages, showcasing the flexibility of the actor model in handling different behaviors.\n\n## State Management - Ref's and Call-by-Name Actor\n\n`become` and `unbecome` can be useful for state management in actors, but they might become cumbersome when the state needs to be updated from multiple places or grows too large. In such cases, using a `Ref` can be a better alternative.\n\n### Example: Using Refs\n\nWe'll create an actor that can switch between two behaviors: `Happy` and `Angry`. The actor will start in the `Happy` state and can switch to the `Angry` state when it receives `MakeAngry`. It can also switch back to the `Happy` state upon receiving a `MakeHappy` message. We'll use `Refs` to manage the state.\n\n```scala\nimport cats.effect.{IO, Ref}\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nsealed trait MoodActorRequests\ncase object MakeAngry extends MoodActorRequests\ncase object MakeHappy extends MoodActorRequests\ncase object Greet extends MoodActorRequests\n\nsealed trait Mood\ncase object Happy extends Mood\ncase object Angry extends Mood\n\ncase class MoodActor(state: Ref[IO, Mood]) extends Actor[IO, MoodActorRequests] {\n\n  override def receive: Receive[IO, MoodActorRequests] = {\n    case MakeAngry =\u003e\n      for {\n        _ \u003c- IO.println(\"Switching to angry state...\")\n        _ \u003c- state.set(Angry)\n      } yield ()\n\n    case MakeHappy =\u003e\n      for {\n        _ \u003c- IO.println(\"Switching to happy state...\")\n        _ \u003c- state.set(Happy)\n      } yield ()\n    case Greet =\u003e\n      state.get.flatMap {\n        case Angry =\u003e\n          IO.println(\"Go away! I'm angry!\")\n        case Happy =\u003e\n          IO.println(\"Hello! I'm happy!\")\n      }\n  }\n}\n\nobject MoodActor {\n  def make: IO[MoodActor] = Ref.of[IO, Mood](Happy).map(MoodActor(_))\n}\n```\n\nNext, let's create an `IOApp` to run the example. We'll create an `ActorSystem` and a `MoodActor`, and then send messages to switch between states and observe the behavior.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nobject MoodActorExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"MoodActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        moodActor \u003c- system.actorOf(MoodActor.make, \"mood-actor\")\n        _ \u003c- moodActor ! Greet // Should print: \"Hello! I'm happy!\"\n        _ \u003c- moodActor ! MakeAngry // Should switch to angry state\n        _ \u003c- moodActor ! Greet // Should print: \"Go away! I'm angry!\"\n        _ \u003c- moodActor ! MakeHappy // Should switch back to happy state\n        _ \u003c- moodActor ! Greet // Should print: \"Hello! I'm happy!\"\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **MoodActor**:\n\n- Uses a `Ref` to manage the state (`Happy` or `Angry`).\n- Handles state transitions directly by updating the `Ref`.\n- Handles messages:\n  - `MakeAngry`: Switches to the `Angry` state and updates the `Ref`.\n  - `MakeHappy`: Switches to the `Happy` state and updates the `Ref`.\n  - `Greet`: Prints a message based on the current state.\n\n2. **MoodActor.make**:\n\n- Creates a `Ref` to hold the initial state (`Happy`).\n- Returns an `IO` instance that creates a `MoodActor` with the `Ref`.\n\n3. **MoodActorExampleApp**:\n\n- Creates an `ActorSystem` and a `MoodActor`.\n- Sends a series of messages to the `MoodActor` to switch between states and observe the behavior:\n  - `Greet`: Prints the current greeting based on the state.\n  - `MakeAngry`: Switches to the angry state.\n  - `MakeHappy`: Switches back to the happy state.\n\n4. **Mood**:\n- `Mood` is a sealed trait representing the state of the actor.\n- It has two possible states: `Happy` and `Angry`.\n- These states are used to determine the actor's behavior when it receives messages.\n\n\n### Example: Inline Alternative\nThe helper method `MoodActor.make` is not necessary because the `actorOf` method can directly accept a call-by-name actor instance or an `Applicative[F]`. This allows the `MoodActor` to be defined inline, streamlining the actor creation process. Below is an example demonstrating this approach:\n\n```scala\nimport cats.effect._\nimport com.suprnation.actor.ActorSystem\n\nobject MoodActorExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"MoodActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        moodActor \u003c- system.actorOf(\n          for {\n            state \u003c- Ref.of[IO, Mood](Happy)\n          } yield MoodActor(state),\n          \"mood-actor\"\n        )\n        _ \u003c- moodActor ! Greet // Should print: \"Hello! I'm happy!\"\n        _ \u003c- moodActor ! MakeAngry // Should switch to angry state\n        _ \u003c- moodActor ! Greet // Should print: \"Go away! I'm angry!\"\n        _ \u003c- moodActor ! MakeHappy // Should switch back to happy state\n        _ \u003c- moodActor ! Greet // Should print: \"Hello! I'm happy!\"\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n## Timeouts in Actors\n\nTimeouts in actor systems are used to schedule actions or messages to be sent after a specified duration. This can be\nuseful for implementing retries, periodic tasks, or handling inactivity.\n\n### `context.setReceiveTimeout`\n\nThe `context.setReceiveTimeout` method allows you to schedule a message to be sent to an actor if no other messages are received within a certain period.\n\n### Example: Using context.setReceiveTimeout\n\nIn this example, we'll create an actor that sets a receive timeout. If the actor does not receive any messages within the specified duration, it will perform a timeout action.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\nimport scala.concurrent.duration._\n\nsealed trait Requests\ncase object Timeout extends Requests\ncase class Message(msg: String) extends Requests\n\ncase class TimeoutActor() extends Actor[IO, Requests] {\n  override def preStart: IO[Unit] =\n    // Set the receive timeout to 5 seconds\n    context.setReceiveTimeout(5.seconds, Timeout)\n\n  override def receive: Receive[IO, Requests] = {\n    case Timeout =\u003e\n      IO.println(\"Timeout occurred! No messages received within the timeout period.\")\n    case Message(msg) =\u003e\n      IO.println(s\"Received message: $msg\")\n  }\n}\n```\n\nNext, let's create an `IOApp` to run the example. We'll create an `ActorSystem` and a `TimeoutActor`, and then send messages to observe the timeout behavior.\n\n```scala\nimport cats.effect.{ExitCode, IO, IOApp, Resource}\nimport com.suprnation.actor.ActorSystem\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\n\nobject TimeoutActorExampleApp extends IOApp {\n  override def run(args: List[String]): IO[ExitCode] = {\n    val actorSystemResource: Resource[IO, ActorSystem[IO]] =\n      ActorSystem[IO](\"TimeoutActorSystem\")\n\n    actorSystemResource.use { system =\u003e\n      for {\n        timeoutActor \u003c- system.actorOf(TimeoutActor(), \"timeout-actor\")\n        _ \u003c- IO.sleep(3.seconds)\n        _ \u003c- timeoutActor ! Message(\"Hello\") // Should print: \"Received message: Hello\"\n        _ \u003c- IO.sleep(6.seconds) // Should trigger the timeout action\n        _ \u003c- timeoutActor ! Message(\n          \"Another message\"\n        ) // Should print: \"Received message: Another message\"\n        _ \u003c- IO.sleep(6.seconds) // Should trigger the timeout action again\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **TimeoutActor**:\n\n- Sets a receive timeout of 5 seconds in the `preStart` method using `context.setReceiveTimeout`.\n- Defines a `receive` method to handle messages:\n  - If a `Timeout` message is received, it prints a timeout message.\n  - For any other message, it prints the received message.\n\n2. **TimeoutActorExampleApp**:\n\n- Creates an `ActorSystem` and a `TimeoutActor`.\n- Sends a `Timeout` message to the `TimeoutActor` after 3 seconds, which should print the received message and reset the timeout.\n- Waits for 6 seconds to trigger the timeout action, which should print the timeout message.\n- Sends another message to the `TimeoutActor`, which should print the received message and reset the timeout.\n- Waits for another 6 seconds to trigger the timeout action again.\n\n\u003e **Note:** The `context.setReceiveTimeout(5.seconds, Timeout)` method schedules the user-defined `Timeout` message to be sent to the actor if no other messages are received within 5 seconds. This allows the actor to perform a specific action (such as logging or cleaning up resources) if it becomes inactive.\n\n## Finite State Machine (FSM) in Actor Systems\nCats-Actors comes with a powerful Finite State Machine (FSM) out of the box, allowing you to manage complex actor behaviors and state transitions seamlessly. By leveraging the FSM, you can define multiple states and transitions based on received messages, ensuring your actors handle various scenarios effectively.\n\n**Key Benefits**\n\n- **State Management:** Easily define and manage multiple states within your actors.\n- **Transitions:** Define transitions based on specific events, making your actor's behavior more predictable and maintainable.\n- **Functional Programming:** Utilize the power of Cats Effect and functional programming principles for robust and clean state management.\n- **Debugging:** Built-in support for debugging and logging state transitions, aiding in development and troubleshooting.\n\n### Example FSM with Debugging Support\n\nFirst, let's define the messages that can be received by our state machine:\n\n```scala\nsealed trait Request\ncase object Start extends Request\ncase object Stop extends Request\n```\n\nNext, let's define the states and data that the FSM will manage.\n\n```scala\nsealed trait State\n\ncase object Idle extends State\n\ncase object Active extends State\n\ncase class Data(counter: Int)\n```\n\nNext, define the FSM using the `FSMBuilder` and include debugging support with `withConsoleInformation`.\n\n```scala\nimport cats.effect._\nimport cats.implicits._\nimport com.suprnation.actor.{ActorSystem, ReplyingActor}\nimport com.suprnation.actor.fsm._\n\nobject ExampleFSMApp extends IOApp {\n\n  sealed trait Request\n  case object Start extends Request\n  case object Stop extends Request\n\n  sealed trait State\n\n  case object Idle extends State\n\n  case object Active extends State\n\n  case class Data(counter: Int)\n\n  override def run(args: List[String]): IO[ExitCode] = {\n    val fsm: IO[ReplyingActor[IO, Request, List[Any]]] = FSM[IO, State, Data, Request, List[Any]]\n      .withConfig(FSMConfig.withConsoleInformation[IO, State, Data, Request, List[Any]])\n      .when(Idle) (stateManager =\u003e { case FSM.Event(Start, data) =\u003e\n        for {\n          newState \u003c- stateManager.goto(Active)\n        } yield newState.using(data.copy(counter = data.counter + 1))\n      })\n      .when(Active) (stateManager =\u003e { case FSM.Event(Stop, data) =\u003e\n        for {\n          newState \u003c- stateManager.goto(Idle)\n        } yield newState.using(data.copy(counter = data.counter + 1))\n      })\n      .onTransition {\n        case (Idle, Active) =\u003e IO.println(\"Transitioning from Idle to Active\")\n        case (Active, Idle) =\u003e IO.println(\"Transitioning from Active to Idle\")\n      }\n      .startWith(Idle, Data(0))\n      .initialize\n\n    ActorSystem[IO](\"example-fsm-system\").use { system =\u003e\n      for {\n        fsmActor \u003c- system.replyingActorOf[Request, List[Any]](fsm)\n        _ \u003c- fsmActor ! Start\n        _ \u003c- fsmActor ! Stop\n        _ \u003c- fsmActor ! Start\n        _ \u003c- fsmActor ! Stop\n        _ \u003c- system.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n### Explanation\n\n1. **Request, States, and Data**:\n\n- `Start` and `Stop` are the messages that can be received by our state machine.\n- `Idle` and `Active` are the states.\n- `Data` holds a counter to track state transitions.\n\n2. **FSM Definition**:\n\n- `FSMBuilder` is used to define state transitions and include debugging support with `FSMConfig.withConsoleInformation`.\n  - `when(Idle)` and `when(Active)` define the state functions for handling events.\n  - `onTransition` defines actions to take during state transitions.\n\n3. **FSM Initialization**:\n\n- `startWith(Idle, Data(0))` initializes the FSM with the `Idle` state and a counter set to 0.\n\n4. **Actor System**:\n\n- An actor system is created, and the FSM is instantiated as an actor.\n- Messages (`Start` and `Stop`) are sent to the FSM actor to trigger state transitions.\n- The actor system waits for termination.\n\n### Highlighted Details\n\n1. **Typed FSM**:\n\nThe FSM is fully typed and will receive messages of type `Request`, which is defined as:\n  ```scala\n  sealed trait Request\n  case object Start extends Request\n  case object Stop extends Request\n  ```\n\nThe response type `List[Any]` is explained further below.\n\n2. **Replying FSM**:\n\n- The FSM may reply to a message based on its corresponding state transition. \n- The reply can be in the form of a returned response that fulfils the ask (`?`) pattern, similarly to any other actor in cats-actors (`ReturnResponse` reply type). \n- For compatibility with the original Akka implementation, the FSM can also reply by sending a message back to the original sender (`SendMessage` reply type).\n- The FSM can also do both, using the `BothMessageAndResponse` reply type.\n- Depending on its implementation, an FSM may not respond at all, or respond with a single, or multiple replies. This is modelled by the requirement for the response type to be monoidal. If no reply is defined, the identity element is returned or sent, otherwise any replies are combined together in one. This is the reason for the response type `List[Any]` used in the example above.\n- Replies can be defined per state, using the `replying` method with the appropriate reply type, with `SendMessage` as the default. A `returning` method is also provided as a default for the `ReturnResponse` type. Subsequent uses of these methods add further replies to the state, which are combined in the monoidal reponse type.\n\n\n## Common Use-cases - Integrate with a messaging queue\n\nIntegrating cats-actors with a queuing system like RabbitMQ is a common scenario. In this example, we'll demonstrate how to push messages from RabbitMQ onto an fs2.Stream and pipe these messages into the actor system for continuous processing.\n\nFirst, let's create an AMQP stream companion object that defines an AMQP (Advanced Message Queuing Protocol) producer and consumer using Cats Effect and RabbitMQ. This object will include methods to create connections and channels, and to produce and consume messages from a RabbitMQ queue.\n\n```scala\nimport cats.effect.std.Queue\nimport cats.effect.unsafe.implicits.global\nimport cats.effect.{IO, Resource}\nimport com.rabbitmq.client._\n\nimport java.nio.charset.StandardCharsets\nimport scala.jdk.CollectionConverters._\n\nobject AmqpStream {\n\n  import fs2.Stream\n\n  val queueName = \"messages\"\n\n  private def createConnection(factory: ConnectionFactory): Resource[IO, Connection] =\n    Resource.make(IO.blocking(factory.newConnection()))(conn =\u003e IO.blocking(conn.close()))\n\n  private def createChannel(connection: Connection): Resource[IO, Channel] =\n    Resource.make(IO.blocking(connection.createChannel()))(channel =\u003e IO.blocking(channel.close()))\n\n  private def createConnectionFactory(\n                                       connectionFactoryConfig: AmqpConnectionFactoryConfig\n                                     ): ConnectionFactory = {\n    val connectionFactory: ConnectionFactory = new ConnectionFactory()\n    connectionFactory.setHost(connectionFactoryConfig.host)\n    connectionFactory.setVirtualHost(connectionFactoryConfig.virtualHost)\n    connectionFactory.setUsername(connectionFactoryConfig.username)\n    connectionFactory.setPassword(connectionFactoryConfig.password)\n    connectionFactory.setPort(connectionFactoryConfig.port)\n    if (connectionFactoryConfig.ssl) connectionFactory.useSslProtocol()\n    connectionFactory\n  }\n\n  def amqpProducer(\n                    connectionFactoryConfig: AmqpConnectionFactoryConfig\n                  ): Resource[IO, String =\u003e IO[Unit]] = {\n    val connectionFactory: ConnectionFactory = createConnectionFactory(connectionFactoryConfig)\n    val amqpProperties: AMQP.BasicProperties = new AMQP.BasicProperties.Builder().build()\n\n    for {\n      connection \u003c- createConnection(connectionFactory)\n      channel \u003c- createChannel(connection)\n      _ \u003c- Resource.eval(\n        IO(channel.queueDeclare(queueName, false, false, false, Map.empty[String, AnyRef].asJava))\n      )\n    } yield (message: String) =\u003e\n      IO(channel.basicPublish(\"\", queueName, amqpProperties, message.getBytes()))\n  }\n\n  def amqpConsumerStream(\n                          connectionFactoryConfig: AmqpConnectionFactoryConfig\n                        ): Stream[IO, String] = {\n    val connectionFactory: ConnectionFactory = createConnectionFactory(connectionFactoryConfig)\n\n    for {\n      connection \u003c- Stream.resource(createConnection(connectionFactory))\n      channel \u003c- Stream.resource(createChannel(connection))\n      queue \u003c- Stream.eval(Queue.unbounded[IO, String])\n      stream \u003c- {\n        val deliverCallback: DeliverCallback = (_, delivery) =\u003e\n          queue.offer(new String(delivery.getBody, StandardCharsets.UTF_8)).unsafeRunSync()\n        Stream\n          .eval(IO {\n            channel.basicQos(2048)\n            channel.basicConsume(queueName, true, deliverCallback, (_: String) =\u003e {})\n          })\n          .drain ++ Stream.fromQueueUnterminated(queue)\n      }\n    } yield stream\n  }\n\n}\n```\n\n### Explanation\n\n#### amqpProducer Method\n\nThe `amqpProducer` method is responsible for creating a producer that can send messages to a RabbitMQ queue. Here's a step-by-step overview:\n\n1. **Connection Factory:** It creates a ConnectionFactory using the provided configuration.\n2. **AMQP Properties:** It defines basic properties for the AMQP messages.\n3. **Resource Management:**\n\n- Connection: It creates a connection to the RabbitMQ server.\n- Channel: It creates a channel on the connection.\n- Queue Declaration: It declares the queue if it doesn't already exist.\n\n4. **Message Publishing:** It returns a function that takes a message as input and publishes it to the queue using the channel.\n\n#### amqpConsumerStream Method\n\nThe `amqpConsumerStream` method is responsible for creating a stream that consumes messages from a RabbitMQ queue. Here's a step-by-step overview:\n\n1. **Connection Factory:** It creates a ConnectionFactory using the provided configuration.\n2. **Stream Management:**\n\n- Connection: It creates a connection to the RabbitMQ server as a stream resource.\n- Channel: It creates a channel on the connection as a stream resource.\n- Queue: It creates an unbounded queue to hold the messages.\n\n3. **Message Consumption:**\n\n- Deliver Callback: It defines a callback to handle message delivery, which offers messages to the queue.\n- Stream Construction:\n  - Quality of Service: It sets the Quality of Service for the channel.\n  - Message Consumption: It starts consuming messages from the queue using the deliver callback.\n  - Stream Composition: It combines the consuming stream with the queue stream to produce a continuous stream of messages.\n\nNow that we've covered the boilerplate, let's create a complete application that pipes messages from an `fs2.Stream` to an actor.\n\n### Example: Processing via a single actor\n\n```scala\nimport cats.effect._\nimport cats.implicits._\nimport com.suprnation.actor.Actor.Receive\nimport com.suprnation.actor.{Actor, ActorSystem}\n\nimport scala.concurrent.duration._\n\nobject SingleActorRabbitApp extends IOApp {\n\n  override def run(args: List[String]): IO[ExitCode] = {\n    val connectionConfig = AmqpConnectionFactoryConfig(\"localhost\", \"/\", \"guest\", \"guest\")\n\n    val resources = for {\n      consumer \u003c- Resource.pure(AmqpStream.amqpConsumerStream(connectionConfig))\n      producer \u003c- AmqpStream.amqpProducer(connectionConfig)\n      actorSystem \u003c- ActorSystem[IO](\"cats-actors-rabbit-sample\")\n    } yield (consumer, producer, actorSystem)\n\n    resources.use { case (consumer, producer, actorSystem) =\u003e\n      for {\n        actor \u003c- actorSystem.actorOf(new Actor[IO, String] {\n          override def receive: Receive[IO, String] = {\n            case msg =\u003e\n              IO.println(msg)\n          }\n        })\n        _ \u003c- consumer.evalTapChunk(actor ! _).compile.drain.start\n        _ \u003c- List(\"1\", \"2\", \"3\", \"4\")\n          .traverse(message =\u003e producer(message).delayBy(10.millis))\n          .foreverM\n          .start\n        _ \u003c- actorSystem.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n#### Explanation\n\n1. Actor Creation:\n\n   ```scala\n   actor\n   \u003c- actorSystem.actorOf(new Actor[IO, String] {\n     override def receive: Receive[IO, String] = {\n       case msg =\u003e\n         IO.println(msg)\n     }\n   })\n   ```\n\n   An actor is created with a receive method that prints any received message.\n2. Piping Data from FS2 Stream to Actor:\n\n   ```scala\n   _ \u003c- consumer.evalTapChunk(actor ! _).compile.drain.start\n   ```\n\n   - `consumer`: This is the FS2 stream that consumes messages from RabbitMQ.\n   - `evalTapChunk(actor ! _)`: This method evaluates an effectful function for each chunk of the stream. Here, it sends each message to the actor using the `!` operator.\n   - `compile.drain.start`: This compiles the stream to an `IO` and starts it concurrently.\n\nThis setup ensures that messages consumed from RabbitMQ are processed by the actor in real-time.\n\n### Example: Processing by sending computations in a round-robin fashion\n\nProcessing all messages within a single actor can be inefficient. A better approach is to distribute the load across\nmultiple actors. One simple strategy for this is to process messages using a set of actors, where each message is\ndistributed to actors in a round-robin fashion.\n\nIn this example, we will create a set of actors and distribute incoming messages among them in a round-robin manner,\nwhich helps in balancing the load and improving the system's responsiveness.\n\n```scala\nimport cats.effect._\nimport cats.implicits._\nimport com.suprnation.actor.Actor.Receive\nimport com.suprnation.actor.{Actor, ActorRef, ActorSystem}\n\nimport java.util.UUID\nimport scala.concurrent.duration._\n\nobject ShardedActorRabbitApp extends IOApp {\n\n  override def run(args: List[String]): IO[ExitCode] = {\n    case class ShardActor() extends Actor[IO, String] {\n      override def receive: Receive[IO, String] = {\n        case msg =\u003e\n          IO.println(s\"[Actor: ${context.self.path.name}] [Message: $msg]\")\n      }\n    }\n\n    val connectionConfig = AmqpConnectionFactoryConfig(\"localhost\", \"/\", \"guest\", \"guest\")\n\n    val resources = for {\n      consumer \u003c- Resource.pure(AmqpStream.amqpConsumerStream(connectionConfig))\n      producer \u003c- AmqpStream.amqpProducer(connectionConfig)\n      actorSystem \u003c- ActorSystem[IO](\"cats-actors-rabbit-sample\")\n    } yield (consumer, producer, actorSystem)\n\n    resources.use { case (consumer, producer, actorSystem) =\u003e\n      for {\n        actor \u003c- actorSystem.actorOf[String, Unit](for {\n          children \u003c- Ref[IO].of[List[ActorRef[IO, String]]](List.empty)\n          index \u003c- Ref[IO].of[Int](1)\n          maxChildren = 10\n        } yield new Actor[IO, String] {\n          override def preStart: IO[Unit] =\n            (1 to maxChildren).toList.traverse(index =\u003e\n              context.actorOf(ShardActor(), s\"child-$index\")\n            ) \u003e\u003e= children.set\n\n          override def receive: Receive[IO, String] = {\n            case m =\u003e\n              (children.get, index.get).flatMapN { case (children, currentIndex) =\u003e\n                (children(currentIndex % maxChildren) ! m) \u003e\u003e index.update(_ + 1)\n              }\n          }\n        })\n        _ \u003c- consumer.evalTapChunk(actor ! _).compile.drain.start\n        _ \u003c- IO\n          .defer(producer(s\"hello-${UUID.randomUUID().toString}\").delayBy(1.millis))\n          .foreverM\n          .start\n        _ \u003c- actorSystem.waitForTermination\n      } yield ExitCode.Success\n    }\n  }\n}\n```\n\n#### Explanation\n\nLet's break down the code with a focus on how the shard of actors is created and how messages are piped to them.\n\n##### ShardActor\n\nThis is a simple actor that prints any received message along with its own name.\n\n```scala\nimport cats.effect.IO\nimport com.suprnation.actor.Actor.{Actor, Receive}\n\ncase class ShardActor() extends Actor[IO, String] {\n  override def receive: Receive[IO, String] = { case msg =\u003e\n    IO.println(s\"[Actor: ${context.self.path.name}] [Message: $msg]\")\n  }\n}\n```\n\n##### Shard Creation\n\n1. Actor Initialization:\n\n```scala\nactor\n\u003c- actorSystem.actorOf[String](for {\n  children \u003c- Ref[IO].of[List[ActorRef[IO, String]]](List.empty)\n  index \u003c- Ref[IO].of[Int](1)\n  maxChildren = 10\n} yield new Actor[IO] {\n```\n\nThe for comprehension creates an `IO[Actor[IO, String]]`. This applicative is processed by actorOf to create the actor.\n\n2. Pre-Start Hook:\n\n```scala\noverride def preStart: IO[Unit] =\n  (1 to maxChildren).toList.traverse(index =\u003e\n    context.actorOf(ShardActor(), s\"child-$index\")\n  ) \u003e\u003e= children.set\n```\n\nInitializes `maxChildren` number of `ShardActor` instances and stores their references in children.\n\n3. Message Handling:\n\n```scala\noverride def receive: Receive[IO, String] = {\n  case m =\u003e\n    (children.get, index.get).flatMapN { case (children, currentIndex) =\u003e\n      (children(currentIndex % maxChildren) ! m) \u003e\u003e index.update(_ + 1)\n    }\n}\n```\n\n`receive`: Distributes incoming messages to child actors in a round-robin fashion using the index.\n\n## Common Scenarios - Sharding of Actors with Timeout\n\nIn this example, we aim to dynamically create actors to handle user-specific tasks and terminate idle actors to reclaim memory. The `ActorSupervisor` creates `ShardActor` instances for each user to process messages. When a `ShardActor` remains idle, it notifies the `ActorSupervisor`, which then terminates the idle actor to optimize memory usage.\n\n```scala\npackage com.suprnation.samples\n\nimport cats.effect.{ExitCode, IO, IOApp}\nimport cats.implicits._\nimport com.suprnation.EscalatingActor\nimport com.suprnation.actor.Actor.Receive\nimport com.suprnation.actor.ActorRef.ActorRef\nimport com.suprnation.actor.{ActorSystem, PoisonPill}\nimport com.suprnation.samples.ShardingApp.ActorSupervisor.{ActorIdle, Request, Shard, Timeout}\n\nimport scala.concurrent.duration._\nimport scala.language.postfixOps\nobject ShardingApp extends IOApp {\n  object ActorSupervisor {\n    trait Request\n    case class Shard(userId: Long, amount: Int) extends Request\n    case class ActorIdle(userId: Long) extends Request\n    case object Timeout extends Request\n  }\n\n  case class ActorSupervisor() extends EscalatingActor[IO, Request] {\n    override val receive: Receive[IO, Request] = receiveWithMap(Map.empty)\n\n    def receiveWithMap(\n        userMap: Map[Long, ActorRef[IO, Request]]\n    ): Receive[IO, Request] = {\n      case g @ Shard(userId, _) =\u003e\n        if (userMap.contains(userId)) {\n          IO.println(s\"User $userId is already found!\") \u003e\u003e (userMap(userId) ! g)\n        } else {\n          for {\n            _ \u003c- IO.println(s\"Oh noes! Actor $userId not found!!\")\n            actorAddress \u003c- context.actorOf(ShardActor(self, userId))\n            _ \u003c- context.become(receiveWithMap(userMap + (userId -\u003e actorAddress)))\n            _ \u003c- actorAddress \u003e\u003e! g\n          } yield ()\n        }\n\n      case ActorIdle(userId) =\u003e\n        IO.println(s\"Actor is idle $userId (parent)\") \u003e\u003e\n          context.become(receiveWithMap(userMap - userId)) \u003e\u003e\n          (userMap(userId) !* PoisonPill)\n    }\n  }\n\n  case class ShardActor(parent: ActorRef[IO, Request], userId: Long)\n      extends EscalatingActor[IO, Request] {\n    override def preStart: IO[Unit] =\n      context.setReceiveTimeout(2 seconds, Timeout)\n\n    override def receive: Receive[IO, Request] = {\n      case Shard(userId, amount) =\u003e\n        IO.println(s\"Received Message from child [UserId: $userId] [Amount: $amount]\")\n      case Timeout =\u003e\n        IO.println(s\"Hey no one sent me anything for [UserId: $userId] 2 seconds!\") \u003e\u003e\n          (parent ! ActorIdle(userId))\n    }\n  }\n\n  override def run(args: List[String]): IO[ExitCode] =\n    ActorSystem[IO]()\n      .use(system =\u003e\n        for {\n          supervisor \u003c- system.actorOf[Request](new ActorSupervisor())\n          _ \u003c- supervisor ! Shard(1, 10)\n          _ \u003c- supervisor ! Shard(2, 20)\n          _ \u003c- supervisor ! Shard(1, 5)\n          _ \u003c- supervisor ! Shard(2, 1)\n          _ \u003c- supervisor ! Shard(1, 10)\n          _ \u003c- supervisor ! Shard(2, 20)\n          _ \u003c- system.wait","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudmark%2Fcats-actors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudmark%2Fcats-actors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudmark%2Fcats-actors/lists"}