{"id":15633953,"url":"https://github.com/knutwalker/typed-actors","last_synced_at":"2025-04-15T01:42:43.339Z","repository":{"id":57744210,"uuid":"41612926","full_name":"knutwalker/typed-actors","owner":"knutwalker","description":"compile-time typechecked akka actors","archived":false,"fork":false,"pushed_at":"2016-03-04T00:44:29.000Z","size":1174,"stargazers_count":144,"open_issues_count":5,"forks_count":7,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-11T03:04:09.829Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://knutwalker.github.io/typed-actors/","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/knutwalker.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-30T01:36:05.000Z","updated_at":"2025-02-12T19:55:07.000Z","dependencies_parsed_at":"2022-08-30T10:51:34.688Z","dependency_job_id":null,"html_url":"https://github.com/knutwalker/typed-actors","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Ftyped-actors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Ftyped-actors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Ftyped-actors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knutwalker%2Ftyped-actors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knutwalker","download_url":"https://codeload.github.com/knutwalker/typed-actors/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248991511,"owners_count":21194893,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":"2024-10-03T10:50:44.338Z","updated_at":"2025-04-15T01:42:43.315Z","avatar_url":"https://github.com/knutwalker.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"![banner](banner.jpg)\n\n[![Build Status][ci-img]][ci]\n[![Coverage][coverage-img]][coverage]\n[![Maven][maven-img]][maven]\n[![Gitter][gitter-img]][gitter]\n[![Apache License][license-img]][license]\n\n**Note**: For Akka 2.4, you have to add `-a24` to the version number. For example `1.1.0` becomes `1.1.0-a24`.\nThe first version available for Akka 2.4 is `1.4.0`.\n\n\u003c!--- TUT:START --\u003e\n```scala\nlibraryDependencies ++= List(\n  \"de.knutwalker\" %% \"typed-actors\" % \"1.6.0\",\n  \"de.knutwalker\" %% \"typed-actors-creator\" % \"1.6.0\"\n)\n```\n\n\n## [Documentation][docs]\n\n- [Motivation](#motivation)\n- [Basic Usage](#basic-usage)\n- [Union typed actors](#union-typed-actors)\n- [Unsafe Usage](#unsafe-usage)\n- [TypedActor](#typedactor)\n- [Building Props](#building-props)\n- [Typed Creator](#typed-creator)\n- [Implementation Notes](#implementation-notes)\n- [Comparison with Akka Typed](#comparison-with-akka-typed)\n\n## Motivation\n\n\nOne critique of Akka, that [comes](https://groups.google.com/d/msg/akka-user/rLKk7-D_jHQ/M_Anx7vRNhcJ) up [every](http://noelwelsh.com/programming/2013/03/04/why-i-dont-like-akka-actors/#akkas-actors-are-not-usefully-typed) [now](http://stew.vireo.org/posts/I-hate-akka/) and [then](http://stackoverflow.com/q/5547947/2996265) is the lack of type safety. Actors essentially represent a `PartialFunction[Any, Unit]` which is, from a type point of view, something of the worst you can have. It tells you nothing useful; Anything can go in, it might or might not be processed and if so, anything anywhere anytime can happen. It forgoes all the benefits of a statically typed language.\nThere are many reasons for this though, amongst others: location transparency and `context.become`. While its true that only `Any` allows us to model _everything_ that _can_ happen, it doesn't mean that everything _will always_ happen. Not every actor gets moved around between different nodes and changes its behavior to something completely unrelated over and over again.\n\nSo, why not tell the compiler that we know something about certain actors and have it help us? We're in a statically typed language after all. We're used to compiler support when it comes to refactoring, design and composition. Why forgo this for the sake of a feature I don't want to use.\n\nHence, `Typed Actors`!\n\nAkka underwent some experiments itself, for example  from [typed-channels](http://doc.akka.io/docs/akka/2.2.0/scala/typed-channels.html) and [typed-actors](http://doc.akka.io/docs/akka/2.3.0/scala/typed-actors.html) to [akka-typed](http://doc.akka.io/docs/akka/2.4.0/scala/typed.html).\nEspecially the last approach, `Akka Typed` is really nice and the benefit of having an `ActorRef[A]` lead to the creation of this library.\n\n`Typed Actors` has the following goals:\n\n- add a compile-time layer to existing `ActorRef`s with minimal runtime overhead\n- be compatible with all of the existing Akka modules, traits, and extensions in terms of composition and behavior\n\nand the following non-goals:\n\n- enforce an impenetrable mantle of types, don't fight the users knowledge about the actor system, those *are* dynamic after all\n- support Java\n\nSo, let's dive in.\n\n\n## Basic Usage\n\n\nTo use **Typed Actors**, import the following:\n\n```scala\nimport de.knutwalker.akka.typed._\n```\n\nThe underscore/wildcard import is important to bring some implicit classes into scope.\nThese classes enable the actual syntax to use typed actors.\nAlso, _Typed Actors_ shadows some names from `akka.actor`, so you need to make sure, that you add this import **after** your akka imports.\n\n```scala\nimport akka.actor._\nimport de.knutwalker.akka.typed._\n```\n\n#### Actor Definition\n\nUsing Typed Actors is, at first, similar to regular actors.\nIt is always a good idea to define your message protocol.\n\n```scala\nsealed trait MyMessage\ncase class Foo(foo: String) extends MyMessage\ncase class Bar(bar: String) extends MyMessage\n\ncase object SomeOtherMessage\n```\n\nWith that, define a regular actor.\n\n```scala\nclass MyActor extends Actor {\n  def receive = {\n    case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n    case Bar(bar) =\u003e println(s\"received a Bar: $bar\")\n  }\n}\n```\n\n#### Actor Creation\n\nNow, use `Props` and `ActorOf`. These are now the ones from `de.knutwalker.akka.typed`, not from `akka.actor`.\n\n```scala\nscala\u003e implicit val system = ActorSystem(\"foo\")\nsystem: akka.actor.ActorSystem = akka://foo\n\nscala\u003e val props = Props[MyMessage, MyActor]\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e val ref = ActorOf(props, name = \"my-actor\")\nref: de.knutwalker.akka.typed.package.ActorRef[props.Message] = Actor[akka://foo/user/my-actor#-260681439]\n```\n\nThis will give you an `ActorRef[MyMessage]`.\n\nThere are three possible ways to create a `Props`, mirroring the constructors from `akka.actor.Props`.\n\n```scala\nscala\u003e val props = Props[MyMessage, MyActor]\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e val props = Props[MyMessage, MyActor](new MyActor)\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e val props = Props[MyMessage, MyActor](classOf[MyActor])\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n```\n\n#### Sending messages\n\nSending messages to a typed actor is the same as sending messages to an untyped on, you use `!`.\n\n```scala\nscala\u003e ref ! Foo(\"foo\")\nreceived a Foo: foo\n\nscala\u003e ref ! Bar(\"bar\")\nreceived a Bar: bar\n```\n\nIf you try to send a message from a different protocol, you will get a compile error. Hooray, benefit!\n\n```scala\nscala\u003e ref ! SomeOtherMessage\n\u003cconsole\u003e:31: error: type mismatch;\n found   : SomeOtherMessage.type\n required: ref.Message\n    (which expands to)  MyMessage\n       ref ! SomeOtherMessage\n             ^\n```\n\n#### Ask pattern\n\nTyped actors support the ask pattern, `?`, without imports and the returned Future is properly typed.\nIn order to achieve this, instead of sending an already instantiated type, you send a function that, given the properly typed sender, will return the message.\nThis is usually achieved with a separate parameter list on a case class (message), typically called `replyTo`.\n\n```scala\ncase class MyResponse(payload: String)\ncase class MyMessage(payload: String)(val replyTo: ActorRef[MyResponse])\n```\n\nIf you define your messages this way, you can left out the last parameter list and will get the required function.\nTo respond, use `message.replyTo` instead of `sender()` to get the properly typed sender. Although, to be fair, `sender()` will be the same actor, it's just the untyped version.\nFinally, `?` requires an `implicit Timeout`, just like the regular, untyped ask.\n\n```scala\nimport scala.concurrent.duration._\nimport akka.util.Timeout\n\nclass MyActor extends Actor {\n  def receive = {\n    case m@MyMessage(payload) =\u003e m.replyTo ! MyResponse(payload)\n  }\n}\nimplicit val timeout: Timeout = 1.second\n```\n\n```scala\nscala\u003e val ref = ActorOf(Props[MyMessage, MyActor])\nref: de.knutwalker.akka.typed.package.ActorRef[MyMessage] = Actor[akka://foo/user/$a#-1898850520]\n\nscala\u003e val future = ref ? MyMessage(\"foo\")\nfuture: scala.concurrent.Future[MyResponse] = scala.concurrent.impl.Promise$DefaultPromise@75cf45c8\n\nscala\u003e val response = scala.concurrent.Await.result(future, 1.second)\nresponse: MyResponse = MyResponse(foo)\n```\n\nNext up, learn how to mix multiple unrelated messages into the checked type.\n\n\n\n\n\n\n## Union typed actors\n\n\n\n\n\n\n\n#### Unrelated Messages\n\nThe actor messages [before](index.html#actor-definition) were defined as a sealed trait so that the actor can deal with all subclasses of this trait. That way the actor can deal with multiple types of messages.\nThis works great if you're in control of the messages, unfortunately this is not always the case. Sometimes you have to write an actor that receives multiple messages that are not part of the same sealed trait, possibly because you don't own said messages.\nTo still use `Typed Actors`, you could use `Any`, which is just as bad as using untyped actors directly.\nAlternatively, you could use a [sum type](https://en.wikipedia.org/wiki/Sum_type) like `Either`, define the actor as `ActorRef[Either[A, B]]` and pattern match on the either in the receive block. This has some drawbacks though.\nFirst, listing more than 2 different messages with Either gets very tedious and you'll probably start writing specific custom sum types for each different set of messages and end up with sealed traits that do nothing but wrap other messages and are thus just noisy boilerplate.\nSecond, there is a runtime overhead involved of wrapping and unwrapping the message in the sum type, i.e. you have to `apply` and `unapply` the `Left`/`Right` instances.\nThird, and probably the most disruptive one, you cannot send any of the summed types directly but have to wrap them at tellsite, coupling the actor to the chosen sum type. This also means, that you cannot write proxy-like actors that sit in-between other actors because you have to change the messages.\n\n`Typed Actors` offer an easy alternative, that solves all the aforementioned problems: **Union Types**.\nBoth, `ActorRef[A]` and `Props[A]`, have a `or[B]` method, that turns those types into an `ActorRef[A | B]` or a `Props[A | B]`, respectively.\n`A | B` is a so called [union type](http://ktoso.github.io/scala-types-of-types/#union-type-span-style-color-red-span) (also sometimes called a disjoint or discriminated union) meaning it is either `A` or `B`. In this regard, it serves the same purpose as `Either[A, B]`, but it is a pure type-level construct. There is no runtime value possible for `A | B`, it is intended to be used a [phantom type](http://ktoso.github.io/scala-types-of-types/#phantom-type) to allow the compiler to apply specific constraints on certain methods.\nYou, as a library user, needn't worry about this; just read `A | B` as `A or B`.\nAs a side note, the implementation is different than the one provided by Miles, referenced in the link above and, dare I say, simpler; it's not based on Curry-Howard isomorphism and doesn't require unicode to type.\n\nYou can call `or` multiple times, creating an ever-growing union type. For example `ActorRef[A].or[B].or[C].or[D]` yields `ActorRef[A | B | C | D]`, which just reads `A or B or C or D`. There is no restriction on the length (certainly not at 22), although compile times will suffer for very large union types.\nThis solves the first problem, enumerating many types just works naturally. To be fair, this is mainly due to the infix notation. You could write `A Either B Either C` as well, but that's just weird while `A | B | C` comes naturally.\nAs mentioned before, `|` is a pure typelevel construct—there is no runtime value, not event a simple wrapper. This fact solves both, the aforementioned second and third issue. Since there is not even a valid runtime representation, there can be no overhead and there is no wrapping required at tellsite.\nOkay, enough theory – lets see union types in action.\n\n#### Union types\n\nFirst, let's define some unrelated messages. Note that these are not part of a sealed trait hierarchy.\n\n```scala\ncase class Foo(foo: String)\ncase class Bar(bar: String)\ncase class Baz(baz: String)\ncase object SomeOtherMessage\n```\n\nNow, let's define an actor that receives all of these messages.\n\n```scala\nclass MyActor extends Actor {\n  def receive = {\n    case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n    case Bar(bar) =\u003e println(s\"received a Bar: $bar\")\n    case Baz(baz) =\u003e println(s\"received a Baz: $baz\")\n  }\n}\n```\n\nDefine a `Props` for one of those messages.\n\n```scala\nscala\u003e val props: Props[Foo] = Props[Foo, MyActor]\nprops: de.knutwalker.akka.typed.Props[Foo] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n```\n\nNow just list the other message types using `or`, either on the `Props` or on a created `ActorRef`.\n\n```scala\nscala\u003e val props2: Props[Foo | Bar] = props.or[Bar]\nprops2: de.knutwalker.akka.typed.Props[de.knutwalker.akka.typed.|[Foo,Bar]] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e val ref2: ActorRef[Foo | Bar] = ActorOf(props2, name = \"my-actor\")\nref2: de.knutwalker.akka.typed.ActorRef[de.knutwalker.akka.typed.|[Foo,Bar]] = Actor[akka://foo/user/my-actor#-1800143858]\n\nscala\u003e val ref: ActorRef[Foo | Bar | Baz] = ref2.or[Baz]\nref: de.knutwalker.akka.typed.ActorRef[de.knutwalker.akka.typed.|[de.knutwalker.akka.typed.|[Foo,Bar],Baz]] = Actor[akka://foo/user/my-actor#-1800143858]\n```\n\nNow you can send either one of the messages that are listed in the union type.\n\n```scala\nscala\u003e ref ! Foo(\"foo\")\nreceived a Foo: foo\n\nscala\u003e ref ! Bar(\"bar\")\nreceived a Bar: bar\n\nscala\u003e ref ! Baz(\"baz\")\nreceived a Baz: baz\n```\n\nAnd if you try to send a message that is not part of the type union, you will get a compile error.\n\n```scala\nscala\u003e ref ! SomeOtherMessage\n\u003cconsole\u003e:32: error: Cannot prove that message of type SomeOtherMessage.type is a member of ref.Message.\n       ref ! SomeOtherMessage\n           ^\n```\n\nAs you can see, there are no wrappers involved. When you send the message, the compiler checks that the message you want to send is part of the union and if this checks succeeds, the compiler will allow the call to `!` (by not failing to compile).\nSince there can be no runtime value of the union type, there is a clear distinction for the dispatch to the check if the message itself is the specified type or a subtype thereof and the check if the message is part of the specified union type.\n\nYou can turn an actor that accepts an union type into of its subcases with `only`:\n\n```scala\nscala\u003e ref.only[Foo]\nres4: de.knutwalker.akka.typed.package.ActorRef[Foo] = Actor[akka://foo/user/my-actor#-1800143858]\n\nscala\u003e ref.only[Bar]\nres5: de.knutwalker.akka.typed.package.ActorRef[Bar] = Actor[akka://foo/user/my-actor#-1800143858]\n\nscala\u003e ref.only[Baz]\nres6: de.knutwalker.akka.typed.package.ActorRef[Baz] = Actor[akka://foo/user/my-actor#-1800143858]\n```\n\nWhich checks the untion type as well.\n\n```scala\nscala\u003e ref.only[SomeOtherMessage]\n\u003cconsole\u003e:31: error: not found: type SomeOtherMessage\n       ref.only[SomeOtherMessage]\n                ^\n```\n\n\nUnion types will return later; for now, the next part is to learn how to interact with the less safer parts of Akka.\n\n\n\n\n\n\n## Unsafe Usage\n\n\n\n\n\n\n```scala\nscala\u003e val typedRef = ActorOf[MyMessage](props, name = \"my-actor\")\ntypedRef: de.knutwalker.akka.typed.ActorRef[MyMessage] = Actor[akka://foo/user/my-actor#-760225152]\n```\n\n#### Autoreceived Messages\n\nSome messages are automatically handled by some actors and need or cannot be provided in the actors type.\nOne example is `PoisonPill`. To sent those kind of messages anyway, use `unsafeTell`.\n\n```scala\nscala\u003e typedRef.unsafeTell(PoisonPill)\n```\n\n#### Switch Between Typed and Untyped\n\nAlso, some Akka APIs require you to pass an untyped ActorRef (the regular ActorRef).\nYou can easily turn your typed actor into an untyped one bu using `untyped`.\n\n```scala\nscala\u003e val untypedRef = typedRef.untyped\nuntypedRef: de.knutwalker.akka.typed.package.UntypedActorRef = Actor[akka://foo/user/my-actor#-760225152]\n```\n\nFor convenience, `akka.actor.ActorRef` is type aliased as `de.knutwalker.akka.typed.UntypedActorRef`.\nSimilarly, you can turn any untyped ref into a typed one using `typed`.\n\n```scala\nscala\u003e val typedAgain = untypedRef.typed[MyMessage]\ntypedAgain: de.knutwalker.akka.typed.package.ActorRef[MyMessage] = Actor[akka://foo/user/my-actor#-760225152]\n```\n\nAs scala tends to infer `Nothing` as the most specific bottom type, you want to make sure to always provide a useful type.\n\n```scala\nscala\u003e untypedRef.typed\nres1: de.knutwalker.akka.typed.package.ActorRef[Nothing] = Actor[akka://foo/user/my-actor#-760225152]\n```\n\n#### Compiletime only\n\nThere are no compiler checks to make sure, that the given actually is able to receive that kind of message.\nThis signifies the point, that **Typed Actors** are really just a compile-time wrapper and do not carry any kind of runtime information.\nTo further demonstrate this, you can see that both instances are actually the very same (despite the scalac warning).\n\n```scala\nscala\u003e typedRef eq untypedRef\n\u003cconsole\u003e:29: warning: AnyRef{type Message = MyMessage; type Self = de.knutwalker.akka.typed.UntypedActorRef} and akka.actor.ActorRef are unrelated: they will most likely never compare equal\n       typedRef eq untypedRef\n                ^\nres2: Boolean = true\n```\n\n#### Divergence\n\nThis also means, that it is possible to diverge from the specified type with `context.become`.\n\n```scala\nscala\u003e class MyOtherActor extends Actor {\n     |   def receive = LoggingReceive {\n     |     case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |     case Bar(bar) =\u003e context become LoggingReceive {\n     |       case SomeOtherMessage =\u003e println(\"received some other message\")\n     |     }\n     |   }\n     | }\ndefined class MyOtherActor\n\nscala\u003e val otherRef = ActorOf(Props[MyMessage, MyOtherActor], \"my-other-actor\")\notherRef: de.knutwalker.akka.typed.package.ActorRef[MyMessage] = Actor[akka://foo/user/my-other-actor#253923972]\n\nscala\u003e otherRef ! Foo(\"foo\")\n[DEBUG] received handled message Foo(foo)\n\nreceived a Foo: foo\nscala\u003e otherRef ! Bar(\"bar\")\n[DEBUG] received handled message Bar(bar)\n\nscala\u003e otherRef ! Foo(\"baz\")\n[DEBUG] received unhandled message Foo(baz)\n\nscala\u003e otherRef.untyped ! SomeOtherMessage\n[DEBUG] received handled message SomeOtherMessage\nreceived some other message\n```\n\nMaking sure, that this cannot happen is outside of the scope of **Typed Actors**.\nThere is, however, a `TypedActor` trait which tries to provide _some_ help. Learn about it next.\n\n\n\n\n\n\n## TypedActor\n\n\n\n\n\n\nHaving a typed reference to an actor is one thing, but how can we improve type-safety within the actor itself?\n**Typed Actors** offers a `trait` called `TypedActor` which you can extend from instead of `Actor`.\n`TypedActor` itself extends `Actor` but contains an abstract type member and typed receive method\ninstead of just an untyped receive method.\nIn order to use the `TypedActor`, you have to extend `TypedActor.Of[_]` and provide your message type via type parameter.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = {\n     |     case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |     case Bar(bar) =\u003e println(s\"received a Bar: $bar\")\n     |   }\n     | }\ndefined class MyActor\n\nscala\u003e val ref = ActorOf(Props[MyMessage, MyActor], name = \"my-actor\")\nref: de.knutwalker.akka.typed.package.ActorRef[MyMessage] = Actor[akka://foo/user/my-actor#-685633214]\n\nscala\u003e ref ! Foo(\"foo\")\nreceived a Foo: foo\n\nscala\u003e ref ! Bar(\"bar\")\nreceived a Bar: bar\n```\n\nIf you match on messages from a different type, you will get a compile error.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = {\n     |     case SomeOtherMessage =\u003e println(\"received some other message\")\n     |   }\n     | }\n\u003cconsole\u003e:22: error: pattern type is incompatible with expected type;\n found   : SomeOtherMessage.type\n required: MyActor.this.Message\n    (which expands to)  MyMessage\n           case SomeOtherMessage =\u003e println(\"received some other message\")\n                ^\n```\n\n\n#### Divergence\n\nSimilar to the untyped actor, `context.become` is not hidden and can still lead to diverging actors.\n\n\n\n\n```scala\nscala\u003e class MyOtherActor extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = {\n     |     case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |     case Bar(bar) =\u003e context become LoggingReceive {\n     |       case SomeOtherMessage =\u003e println(\"received some other message\")\n     |     }\n     |   }\n     | }\ndefined class MyOtherActor\n\nscala\u003e val otherRef = ActorOf(Props[MyMessage, MyOtherActor], \"my-other-actor\")\notherRef: de.knutwalker.akka.typed.package.ActorRef[MyMessage] = Actor[akka://foo/user/my-other-actor#-1609403868]\n\nscala\u003e otherRef ! Foo(\"foo\")\n\nscala\u003e otherRef ! Bar(\"bar\")\n[DEBUG] received handled message Foo(foo)\nreceived a Foo: foo\n[DEBUG] received handled message Bar(bar)\n\nscala\u003e otherRef ! Foo(\"baz\")\n[DEBUG] received unhandled message Foo(baz)\n\nscala\u003e otherRef.untyped ! SomeOtherMessage\n[DEBUG] received handled message SomeOtherMessage\nreceived some other message\n```\n\n#### More Typing\n\nThe `TypedActor` offers some more methods that ought to help with keeping within the defined type bound.\nThere is `typedSelf`, which is the typed version of the regular `self`.\nThen there is `typedBecome`, the typed version of `context.become`. It takes a partial receive function, much like `typedReceive`.\n\nUsing `typedBecome`, diverging from the type bound is no longer possible\n\n```scala\nscala\u003e class MyOtherActor extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = {\n     |     case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |     case Bar(bar) =\u003e typedBecome {\n     |       case SomeOtherMessage =\u003e println(\"received some other message\")\n     |     }\n     |   }\n     | }\n\u003cconsole\u003e:31: error: pattern type is incompatible with expected type;\n found   : SomeOtherMessage.type\n required: MyOtherActor.this.Message\n    (which expands to)  MyMessage\n             case SomeOtherMessage =\u003e println(\"received some other message\")\n                  ^\n```\n\nYou can event get exhaustiveness checks from the compiler by using the `Total` wrapper.\n\n```scala\nscala\u003e class MyOtherActor extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = Total {\n     |     case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |   }\n     | }\n\u003cconsole\u003e:25: warning: match may not be exhaustive.\nIt would fail on the following input: Bar(_)\n         def typedReceive = Total {\n                                  ^\ndefined class MyOtherActor\n```\n\nPlease be aware of a ~~bug~~ feature that wouldn't fail on non-exhaustive checks.\nIf you use guards in your matchers, the complete pattern is optimistically treated as exhaustive; See [SI-5365](https://issues.scala-lang.org/browse/SI-5365), [SI-7631](https://issues.scala-lang.org/browse/SI-7631), and [SI-9232](https://issues.scala-lang.org/browse/SI-9232). Note the missing non-exhaustiveness warning in the next example.\n\n```scala\nscala\u003e val False = false\nFalse: Boolean = false\n\nscala\u003e class MyOtherActor extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = Total {\n     |     case Foo(foo) if False =\u003e\n     |   }\n     | }\ndefined class MyOtherActor\n```\n\nUnfortunately, this cannot be worked around by library code. Even worse, this would not result in a unhandled message but in a runtime match error.\n\n#### Working with Union Types\n\nUnion typed [before](#union-typed-actors) were declared on an already existing `Props` or `ActorRef` but how can we use union types together with `TypedActor`?\n\n```scala\ncase class Foo(foo: String)\ncase class Bar(bar: String)\ncase class Baz(baz: String)\ncase object SomeOtherMessage\n```\n\n(We're shadowing the previous definition of `Foo` and `Bar` here, they are reverted after this chapter).\n\nSince union types are implemented at the type-level, there is no runtime value possible that would allow us to discriminate between those subtypes when running the receive block.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = {\n     |     case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |     case Bar(bar) =\u003e println(s\"received a Bar: $bar\")\n     |     case Baz(baz) =\u003e println(s\"received a Baz: $baz\")\n     |   }\n     | }\n\u003cconsole\u003e:29: error: constructor cannot be instantiated to expected type;\n found   : Foo\n required: de.knutwalker.akka.typed.|[de.knutwalker.akka.typed.|[Foo,Bar],Baz]\n           case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n                ^\n\u003cconsole\u003e:30: error: constructor cannot be instantiated to expected type;\n found   : Bar\n required: de.knutwalker.akka.typed.|[de.knutwalker.akka.typed.|[Foo,Bar],Baz]\n           case Bar(bar) =\u003e println(s\"received a Bar: $bar\")\n                ^\n\u003cconsole\u003e:31: error: constructor cannot be instantiated to expected type;\n found   : Baz\n required: de.knutwalker.akka.typed.|[de.knutwalker.akka.typed.|[Foo,Bar],Baz]\n           case Baz(baz) =\u003e println(s\"received a Baz: $baz\")\n                ^\n```\n\nWe have to do this discrimination at type-level as well. Don't worry, it's less complicated as that sound. As a side note, sum types like `Either` are sometimes referred to as tagged union, the tag being the thing that would help us to discrimite at runtime – our union type is an untagged union instead.\n\nThe basics stay the same, you still extends `TypedActor.Of` and implement `typedReceive` but this time using either `Union` or `TotalUnion`. Use `Union` if you only cover some of the union types cases and `TotalUnion` if you want to cover _all_ cases. The compiler can perform exhaustiveness checks on the latter.\nBoth methods return a builder-style object that has an `on` method that must be used to enumerate the individual subcases of the union type and you close with a call to `apply`.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = Union\n     |     .on[Foo]{ case Foo(foo) =\u003e println(s\"received a Foo: $foo\") }\n     |     .on[Bar]{ case Bar(bar) =\u003e println(s\"received a Bar: $bar\") }\n     |     .on[Baz]{ case Baz(baz) =\u003e println(s\"received a Baz: $baz\") }\n     |     .apply\n     | }\ndefined class MyActor\n```\n\nOr if you have a total function for the cases, there is a shortcut:\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = Union\n     |     .total[Foo]{ foo ⇒ println(s\"received a Foo: $foo.foo\") }\n     |     .total[Bar]{ bar ⇒ println(s\"received a Bar: $bar.bar\") }\n     |     .total[Baz]{ baz ⇒ println(s\"received a Baz: $baz.baz\") }\n     |     .apply\n     | }\ndefined class MyActor\n```\n\nYou have to provide at least one case, you cannot define an empty behavior.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = Union\n     |     .apply\n     | }\n\u003cconsole\u003e:29: error: Cannot prove that de.knutwalker.akka.typed.TypedActor.MkPartialUnionReceive.Empty =:= de.knutwalker.akka.typed.TypedActor.MkPartialUnionReceive.NonEmpty.\n           .apply\n            ^\n```\n\n\nIf you remove one of those cases it still compiles, since `Union` does not check for exhaustiveness.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = Union\n     |     .on[Foo]{ case Foo(foo) =\u003e println(s\"received a Foo: $foo\") }\n     |     .on[Baz]{ case Baz(baz) =\u003e println(s\"received a Baz: $baz\") }\n     |     .apply\n     | }\ndefined class MyActor\n```\n\nIf you switch to `TotalUnion` you can see the compiler message telling that something is missing. Unfortunately it doesn't tell you _which_ case is missing exactly, although that might change in the future.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = TotalUnion\n     |     .on[Foo]{ case Foo(foo) =\u003e println(s\"received a Foo: $foo\") }\n     |     .on[Baz]{ case Baz(baz) =\u003e println(s\"received a Baz: $baz\") }\n     |     .apply\n     | }\n\u003cconsole\u003e:31: error: Cannot prove that de.knutwalker.akka.typed.|[Foo,Baz] contains the same members as de.knutwalker.akka.typed.|[de.knutwalker.akka.typed.|[Foo,Bar],Baz].\n           .apply\n            ^\n```\n\nYou can even leave out the call to `apply`.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = Union\n     |     .on[Foo]{ case Foo(foo) ⇒ println(s\"received a Foo: $foo\") }\n     |     .on[Baz]{ case Baz(baz) ⇒ println(s\"received a Baz: $baz\") }\n     | }\ndefined class MyActor\n```\n\nWhich is true for `TotalUnion` as well.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = TotalUnion\n     |     .on[Foo]{ case Foo(foo) ⇒ println(s\"received a Foo: $foo\") }\n     |     .on[Bar]{ case Bar(bar) ⇒ println(s\"received a Bar: $bar\") }\n     |     .on[Baz]{ case Baz(baz) ⇒ println(s\"received a Baz: $baz\") }\n     | }\ndefined class MyActor\n```\n\nAs you can see, you basically provide a receive block for all relevant subtypes of the union. One such receive block is typed in its input, though you cannot use the `Total` helper as this one is fixed on the complete message type, the union type itself in this case.\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = Union\n     |     .on[Foo](Total { case Foo(foo) =\u003e println(s\"received a Foo: $foo\") })\n     |     .apply\n     | }\n\u003cconsole\u003e:29: error: constructor cannot be instantiated to expected type;\n found   : Foo\n required: de.knutwalker.akka.typed.|[de.knutwalker.akka.typed.|[Foo,Bar],Baz]\n           .on[Foo](Total { case Foo(foo) =\u003e println(s\"received a Foo: $foo\") })\n                                 ^\n```\n\nAt any rate, the `Props` and `ActorRef` from this `TypedActor` are union typed as well.\n\n```scala\nscala\u003e val props = PropsFor[MyActor]\nprops: de.knutwalker.akka.typed.Props[MyActor#Message] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e val ref = ActorOf(props)\nref: de.knutwalker.akka.typed.package.ActorRef[props.Message] = Actor[akka://foo/user/$a#222597801]\n\nscala\u003e ref ! Foo(\"foo\")\n[DEBUG] received handled message Foo(foo)\nreceived a Foo: foo\n\nscala\u003e ref ! Bar(\"bar\")\n[DEBUG] received handled message Bar(bar)\nreceived a Bar: bar\n\nscala\u003e ref ! Baz(\"baz\")\n[DEBUG] received handled message Baz(baz)\nreceived a Baz: baz\n```\n\n```scala\nscala\u003e ref ! SomeOtherMessage\n\u003cconsole\u003e:32: error: Cannot prove that message of type SomeOtherMessage.type is a member of ref.Message.\n       ref ! SomeOtherMessage\n           ^\n```\n\n\nIf you want to `context.become` with a union type there are some options.\n\n1. You can use the `Union`/`TotalUnion` helper as described earlier.\n2. You can use `unionBecome` if you only want to cover _one_ particular case.\n   It is a shortcut for `typedBecome(Union.on[Msg]{ case ... }.apply)`\n\n\n```scala\nscala\u003e class MyActor extends TypedActor.Of[Foo | Bar | Baz] {\n     |   def typedReceive: TypedReceive = Union\n     |     .on[Foo]{\n     |        case Foo(foo) =\u003e\n     |        unionBecome.on[Bar] {\n     |          case Bar(bar) =\u003e println(s\"received a Boo: $bar\")\n     |        }\n     |     }\n     |     .apply\n     | }\ndefined class MyActor\n```\n\n\n#### Stateless actor from a total function\n\n\n\n\nThe companion object `TypedActor` has an `apply` method that wraps a total function in an actor and returns a prop for this actor.\n\n```scala\nscala\u003e val ref = ActorOf(TypedActor[MyMessage] {\n     |   case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |   case Bar(bar) =\u003e println(s\"received a Bar: $bar\")\n     | })\nref: de.knutwalker.akka.typed.package.ActorRef[MyMessage] = Actor[akka://foo/user/$b#509742201]\n```\n\n\n#### Low-level TypedActor\n\nYou can also directly extend `TypedActor`, in which case you have to implement the abstract type `Message`. The `Of` constructor just does this for you by getting all information from the defined type parameter.\nYou want to use this you need the `TypedActor` as a trait, for example when mixing it together with other Actor traits, like `PersistenActor`.\nFor normal use-case, extending `TypedActor.Of[_]` is encouraged.\n\n\n```scala\nscala\u003e import scala.reflect.classTag\nimport scala.reflect.classTag\n\nscala\u003e class MyTypedActor extends TypedActor {\n     |   type Message = MyMessage\n     | \n     |   def typedReceive = {\n     |     case Foo(foo) =\u003e\n     |   }\n     | }\ndefined class MyTypedActor\n```\n\nYou can even override the `receive` method, if you have to, using the `untypedFromTyped` method.\n\n```scala\nscala\u003e class MyTypedActor extends TypedActor {\n     |   type Message = MyMessage\n     | \n     |   override def receive =\n     |     untypedFromTyped(typedReceive)\n     | \n     |   def typedReceive = {\n     |     case Foo(foo) =\u003e\n     |   }\n     | }\ndefined class MyTypedActor\n```\n\nUsing this, you can mix a `TypedActor` and a `PersistentActor` together.\n\n```scala\nscala\u003e import akka.persistence.PersistentActor\nimport akka.persistence.PersistentActor\n\nscala\u003e class TypedPersistentActor extends TypedActor with PersistentActor with ActorLogging {\n     |   type Message = MyMessage\n     | \n     |   def persistenceId: String = \"typed-persistent-id\"\n     | \n     |   val receiveRecover: Receive = akka.actor.Actor.emptyBehavior\n     | \n     |   val typedReceive: TypedReceive = {\n     |     case foo: Foo =\u003e\n     |       persist(foo)(f =\u003e context.system.eventStream.publish(foo))\n     |   }\n     | \n     |   val receiveCommand: Receive =\n     |     untypedFromTyped(typedReceive)\n     | \n     |   override def receive: Receive =\n     |     receiveCommand\n     | }\ndefined class TypedPersistentActor\n```\n\n\n#### Going back to untyped land\n\nSometimes you have to receive messages that are outside of your protocol. A typical case is `Terminated`, but other modules and patterns have those messages as well.\nYou can use `Untyped` to specify a regular untyped receive block, just as if `receive` were actually the way to go. `Untyped` also works with union types without any special syntax.\n\n\n```scala\nscala\u003e class MyOtherActor extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = Untyped {\n     |     case Terminated(ref) =\u003e println(s\"$ref terminated\")\n     |     case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n     |   }\n     | }\ndefined class MyOtherActor\n```\n\nWith `Untyped`, you won't get any compiler support, it is meant as an escape hatch; If you find yourself using `Untyped` all over the place, consider just using a regular `Actor` instead.\n\nNext, learn more ways to create `Props`.\n\n\n\n\n\n\n## Building Props\n\n\n\n\n\n\n#### Message Type Derivation\n\nWhen creating a props for a `TypeActor`, we can derive the message type and thus reduce the amount of type annotation we have to write.\nThis is done with `PropsFor`.\n\nConsider this typed actor.\n\n```scala\nclass MyActor extends TypedActor.Of[MyMessage] {\n  def typedReceive = {\n    case Foo(foo) =\u003e println(s\"received a Foo: $foo\")\n  }\n}\n```\n\nUsing `Props` we have to repeat the information, that this actor only accepts messages of type `MyMessage`, although the compiler knows about this.\n\n```scala\nscala\u003e Props[MyMessage, MyActor] // MyMessage is repetitive\nres0: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e Props(new MyActor) // message type derives as Nothing\nres1: de.knutwalker.akka.typed.package.Props[Nothing] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e Props[MyMessage, MyActor](new MyActor) // MyMessage and MyActor are repetitive\nres2: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e Props(classOf[MyActor]) // message type derives as Nothing\nres3: Object{type Message = Nothing; type Self = akka.actor.Props} = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e Props[MyMessage, MyActor](classOf[MyActor]) // MyMessage and MyActor are repetitive\nres4: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n```\n\nWhen you have a `TypedActor`, you can use `PropsFor` instead of `Props` to use the type information embedded in `TypedActor#Message`.\n\n```scala\nscala\u003e PropsFor[MyActor]\nres5: de.knutwalker.akka.typed.Props[MyActor#Message] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e PropsFor(new MyActor)\nres6: de.knutwalker.akka.typed.package.Props[MyActor#Message] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e PropsFor(classOf[MyActor])\nres7: Object{type Message = MyMessage; type Self = akka.actor.Props} = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n```\n\nOf course, some of these cases can also be mitigated by using type ascription on the result type.\n\n```scala\nscala\u003e val props: Props[MyMessage] = Props(new MyActor)\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e val props: Props[MyMessage] = Props(classOf[MyActor])\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e val props: Props[MyMessage] = PropsFor[MyActor]\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e val props: Props[MyMessage] = PropsFor(new MyActor)\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e val props: Props[MyMessage] = PropsFor(classOf[MyActor])\nprops: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n```\n\n#### Type Currying for Props\n\n`PropsFor` only works with a `TypedActor`. There is yet another way to create a `Props`, that has the type information curried, `PropsOf`.\nWith `PropsOf`, you apply once with the message type and then use one of the three ways to create a `Props`. This works for all actors\n\n```scala\nscala\u003e PropsOf[MyMessage][MyActor]\nres8: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n\nscala\u003e PropsOf[MyMessage](new MyActor)\nres9: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e PropsOf[MyMessage](classOf[MyActor])\nres10: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class MyActor,List())\n```\n\nNext, look at how you can improve type safety even further.\n\n\n\n\n\n## Typed Creator\n\n\n\n\n\nWhen creating a `Props`, the preferred way is to use the `(Class[_], Any*)` overload, since this one does not create a closure.\nIf you create a props from within an Actor using the `(=\u003e Actor)` overload, you accidentally close over the `ActorContext`, that's shared state you don't want.\nThe problem with the constructor using `Class`, you don't get any help from the compiler. If you change one parameter, there is nothing telling you to change the Props constructor but the eventual runtime error (from your tests, hopefully).\n\nUsing shapeless, we can try to fix this issue.\n\n#### Using the creator module\n\nThe types creator lives in a [separate module](http://search.maven.org/#search%7Cga%7C1%7Cg:%22de.knutwalker%22%20AND%20a:typed-actors-creator*) that you have to include first.\n\n```scala\nlibraryDependencies += \"de.knutwalker\" %% \"typed-actors-creator\" % \"1.6.0\"\n```\n\nNext, you _have_ to use the [`TypedActor`](#typedactor) trait and you _have_ to make your actor a `case class`.\nThis is necessary, so that shapeless' generic machinery can pick up the required constructor parameters.\n\n```scala\ncase class MyActor(param: String) extends TypedActor.Of[MyMessage] {\n  def typedReceive = {\n    case Foo(foo) =\u003e println(s\"$param - received a Foo: $foo\")\n    case Bar(bar) =\u003e println(s\"$param - received a Bar: $bar\")\n  }\n}\n```\n\nNext, use the `Typed` constructor. It takes one type parameter, which is supposed to be your `TypedActor`.\nNow you can use two methods, `props` and `create`. Both accept the same arguments as the constructor of your `TypedActor` and will either return a typed `Props` or typed `ActorRef`, respectively (thanks to some shapeless magic).\n\n```scala\nscala\u003e Typed[MyActor].props(\"Bernd\")\nres0: de.knutwalker.akka.typed.Props[MyMessage] = Props(Deploy(,Config(SimpleConfigObject({})),NoRouter,NoScopeGiven,,),class akka.actor.TypedCreatorFunctionConsumer,List(class MyActor, \u003cfunction0\u003e))\n\nscala\u003e Typed[MyActor].create(\"Bernd\")\nres1: de.knutwalker.akka.typed.ActorRef[MyMessage] = Actor[akka://foo/user/$a#-228053190]\n\nscala\u003e ActorOf(Typed[MyActor].props(\"Bernd\"), \"typed-bernd\")\nres2: de.knutwalker.akka.typed.package.ActorRef[MyMessage] = Actor[akka://foo/user/typed-bernd#1603161306]\n```\n\nWrong invocations are greeted with a compile error instead of a runtime error!\n\n```scala\nscala\u003e Typed[MyActor].create()\n\u003cconsole\u003e:26: error: type mismatch;\n found   : shapeless.HNil\n required: shapeless.::[String,shapeless.HNil]\n       Typed[MyActor].create()\n                            ^\n\nscala\u003e Typed[MyActor].create(\"Bernd\", \"Ralf\")\n\u003cconsole\u003e:26: error: type mismatch;\n found   : shapeless.::[String(\"Bernd\"),shapeless.::[String(\"Ralf\"),shapeless.HNil]]\n required: shapeless.::[String,shapeless.HNil]\n       Typed[MyActor].create(\"Bernd\", \"Ralf\")\n                            ^\n\nscala\u003e Typed[MyActor].create(42)\n\u003cconsole\u003e:26: error: type mismatch;\n found   : shapeless.::[Int(42),shapeless.HNil]\n required: shapeless.::[String,shapeless.HNil]\n       Typed[MyActor].create(42)\n                            ^\n```\n\nHooray, Benefit!\n\nAs you can see, shapeless leaks in the error messages, but you can still easily see what parameters are wrong.\nThis technique uses whitebox macros under the hood, which means that support from IDEs such as IntelliJ will be meager, so prepare for red, squiggly lines.\nIf you open autocomplete on a `Typed[MyActor]`, you won't see the `create` or `props` methods but `createProduct` and `propsProduct`. This is a leaky implementation as well, better just ignore it and type against those IDE errors.\n\n\nThe next bits are about the internals and some good pratices..\n\n\n\n\n\n\n## Implementation Notes\n\n\n\n\n\nTyped Actors are implemented as a type tag, a structural type refinement.\nThis is very similar to [`scalaz.@@`](https://github.com/scalaz/scalaz/blob/81e68e845e91b54450a4542b19c1378f06aea861/core/src/main/scala/scalaz/package.scala#L90-L101) and a little bit to [`shapeless.tag.@@`](https://github.com/milessabin/shapeless/blob/6c659d253ba004baf74e20d5d815729552677303/core/src/main/scala/shapeless/typeoperators.scala#L28-L29)\nThe message type is put together with the surrounding type (`ActorRef` or `Props`) into a special type, that exists only at compile time.\nIt carries enough type information for the compiler reject certain calls to tell while not requiring any wrappers at runtime.\n\nThe actual methods are provided by an implicit ops wrapper that extends AnyVal, so that there is no runtime overhead as well.\n\nThe union type is inspired by shapeless' `HNil` or `Coproduct`. The main differences are: 1) There is no runtime, value-level representation and as such, there is no Inr/Inl/:: constructor, it's just the type `|` (instead of `::` or `:+:` for HList and Coproduct, respectively). 2) It doesn't have an end type, a base case like `HNil` or `CNil`. Other than that, the operations around the union type are similar to what you would write if you'd define a function for an HList: There is a typeclass representing the function and some implicit induction steps that recurse on the type.\nThere are some other union type implementations out there, including the one that is offered by shapeless itself but they often just focus on offering membership testing as functionality, while `Typed Actors` also includes a union set comparison to check whether two union types cover the same elements without them being defined in the same order.\n\n#### Good Practices\n\nTyped Actors does not try to prevent you from doing fancy things and shooting yourself in the foot, it rather wants to give you a way so you can help yourself in keeping your sanity.\nThat is, you can always switch between untyped and typed actors, even if the type information is not actually corresponding to the actors implementation. It is up to you to decide how much safety you want to trade in for flexibility.\nThat being said, you get the most benefit by using the [TypedActor](#typedactor) with the [Typed Creator](#typed-creator) and only on the `typedReceive` and `typedBecome` methods with the `Total` wrapper. Depending on the situation, you can fairly fine-tune the amount of untypedness you want to have.\n\nOne other thing that is frequently causing trouble is `sender()`.\nFor one, it's not referentially transparent, return the sender of whatever message the Actor is currently processing. This is causing trouble when the `sender()` call happens for example in a callback attached to a `Future`.\nThe other thing is, it's always an untyped actor and knowledge about the protocol has to be implicitly kept in the head of the developer.\nFor that reasons, it is a good idea to always provide a `replyTo: ActorRef[A]` field in the message itself and refrain from using `sender()`, ideally ever.\n\nAn example of how this could look like. First, the counter example using `sender()` as a quasi status quo.\nTo have a sensible `sender()` available, we're gonna use `akka.actor.Inbox`.\n\n```scala\nimport akka.actor.ActorDSL._\nval box = inbox()\n```\n\nThis is a typical request reply cycle using `sender()`.\n\n```scala\ncase class MyMessage(payload: Int)\ncase class MyResponse(payload: String)\ncase class MyActor() extends TypedActor.Of[MyMessage] {\n  def typedReceive = {\n    case MyMessage(payload) =\u003e sender() ! payload.toString\n  }\n}\n```\n\n```scala\nscala\u003e val ref = Typed[MyActor].create()\nref: de.knutwalker.akka.typed.ActorRef[MyMessage] = Actor[akka://foo/user/$a#466697111]\n\nscala\u003e box.send(ref.untyped, MyMessage(42))\n```\n\nNote that there already is a bug, as the return message was not wrapped in `MyResponse`.\n\n```scala\nscala\u003e val MyResponse(response) = box.receive()\nscala.MatchError: 42 (of class java.lang.String)\n  ... 394 elided\n```\n\nHere's how that looks using the `replyTo` pattern.\n\n```scala\ncase class MyResponse(payload: String)\ncase class MyMessage(payload: Int)(val replyTo: ActorRef[MyResponse])\ncase class MyActor() extends TypedActor.Of[MyMessage] {\n  def typedReceive = {\n    case m@MyMessage(payload) =\u003e m.replyTo ! MyResponse(payload.toString)\n  }\n}\n```\n\n```scala\nscala\u003e val ref = Typed[MyActor].create()\nref: de.knutwalker.akka.typed.ActorRef[MyMessage] = Actor[akka://foo/user/$b#1300424475]\n\nscala\u003e ref ! MyMessage(42)(box.receiver.typed)\n\nscala\u003e val MyResponse(response) = box.receive()\nresponse: String = 42\n```\n\nLet's try to reproduce the bug from earlier.\n\n```scala\nscala\u003e case class MyActor() extends TypedActor.Of[MyMessage] {\n     |   def typedReceive = {\n     |     case m@MyMessage(payload) =\u003e m.replyTo ! payload.toString\n     |   }\n     | }\n\u003cconsole\u003e:27: error: type mismatch;\n found   : String\n required: m.replyTo.Message\n    (which expands to)  MyResponse\n           case m@MyMessage(payload) =\u003e m.replyTo ! payload.toString\n                                                            ^\n```\n\nNow the compiler has caught the bug, benefit!\n\nThe `replyTo` pattern is also important in [Akka Typed](http://doc.akka.io/docs/akka/snapshot/scala/typed.html).\n\n\n\n\n\n## Comparison with Akka Typed\n\n\nThe [`Akka Typed`](http://doc.akka.io/docs/akka/snapshot/scala/typed.html) project is a module of Akka (as of 2.4) which aims to provide typesafe actors as well.\nAkka typed takes a completely different approach, mirroring most of the untyped API and ultimately offering a completely new API to define your actors behavior. Currently, this implementation sits on top of untyped They are currently actors.\nLet me add that I really like Akka Typed and having worked with it for some time lead me to think about how to bring type safety to the rest of Akka.\n\n`Akka Typed` is not only about a typed `ActorRef[A]`, there's much more that's changed and is reason to use `Akka Typed`, both in general and over `Typed Actors`. It separates the behavior of your actors from its execution model, making them really easy to test; You can just use a synchronous stub execution model and you get to test just the behavior without concerning yourself about the how-to-test-this-async-thingy. The new behavior API is not just a convoluted `PartialFunction[A, Unit]` but allows you to split your behavior into nice little pieces and have them composed together. `Akka Typed`'s getting rid of some old (and bad) habits as well; `sender()` is gone, as are lifecycle methods that have to be overridden, even the `Actor` trait itself is gone. It's messages and behavior all the way down!\n\nThose are all concerns that `Typed Actor` will never deal with, this is one important difference: `Typed Actors` is a possibility to add some compile-time checking while `Akka Typed` is a completely new API. Understandingly, `Akka Typed` is better at hiding their untyped implementation, nothing in the public API leads to the fact that something like an untyped actor could even exist.\n\nOn the other hand, having `Akka Typed` as a separate module means it is difficult to use the typed API with other modules. Most APIs expect an `akka.actor.ActorRef` and you can't get one from a akka-typed actor (well, you can, but it's dirty). This also applies to things like `ActorLogging` and `Stash`.\n`Typed Actors` doesn't try to prevent you from going untyped and as there is no different runtime representation, it can be easily used with all existing akka modules.\nHowever, if you mix typed/untyped code too much, you run into unhandled messages or maybe even runtime class cast exceptions or match errors (which ought to be bugs then, really).\n\n`Typed Actors` makes it easy to deal with multiple types of messages, not just one `A` thanks to its [Union type](#union-typed-actors) support. Joining multiple behavior requires them to be of the same type, although you can get far with a little bit of type-fu. Basically, you can take advantage of the covariant nature of `ActorRef[-A]` (in `Typed Actors`, ActorRef is actually invariant) and create phantom intersection types (`A with B`) and upcast at tellsite. It is, however, something different whether you as the library user has to know how to fu or I as the library author know so you don't have to.\n\nAlso, `Akka Typed` is concerned with Java interop, which `Typed Actors` is not.\nNevertheless, `Akka Typed` is a – in my opinion – really nice project and its new API is a major improvement over the default `Actor`. The resulting patterns, like `replyTo` are a good idea to use with `Typed Actor`s as well.\n\nThat concludes the Usage Guide. I guess the only thing left is to go on hAkking!\n\u003c!--- TUT:END --\u003e\n\n## Disclaimer\n\nTyped Actor is operating on a best-effort basis to catch unsafe usages of actors at compile time, as such, it may fail to succeed in this effort.\nIt is no replacement for tests and some interoperability issue when combining multiple scala features can lead to code that should not compile, but passes anyway;\nTherefore the author disclaims all warranty or liability of any kind.\n\n\n### Know unpleasantries\n\nDue to `PartialFunction` being contravariant in its input, `Receive \u003c: TypedReceive` is true.\nIf you have a receive block that is statically known to be a `Receive` it can be passed without any warning to `typedReceive` or other methods that actually accept a `TypedReceive`.\nThis is a failed compiler check, there is no runtime error (if it is, it's a bug).\n\nScala sometime fails to perform exhaustiveness checks for total functions an various grounds. For example, `val f: String =\u003e Unit = { case \"foo\" =\u003e () }` compiles just fine.\nThe exhaustiveness guarantees of various `total` combinators rely soley on scalac for their checks. Therefore, if scalac doesn't warn, Typed Actors also doesn't warn.\n\n\n## License\n\nThis code is open source software licensed under the Apache 2.0 License.\n\n\n[ci-img]: https://img.shields.io/travis/knutwalker/typed-actors/master.svg\n[coverage-img]: https://img.shields.io/codecov/c/github/knutwalker/typed-actors/master.svg\n[maven-img]: https://img.shields.io/maven-central/v/de.knutwalker/typed-actors_2.11.svg?label=latest\n[gitter-img]: https://img.shields.io/badge/gitter-Join_Chat-1dce73.svg\n[license-img]: https://img.shields.io/badge/license-APACHE_2-green.svg\n\n[ci]: https://travis-ci.org/knutwalker/typed-actors\n[coverage]: https://codecov.io/github/knutwalker/typed-actors\n[maven]: http://search.maven.org/#search|ga|1|g%3A%22de.knutwalker%22%20AND%20a%3Atyped-actors*_2.11\n[gitter]: https://gitter.im/knutwalker/typed-actors\n[license]: https://www.apache.org/licenses/LICENSE-2.0\n\n[docs]: http://knutwalker.github.io/typed-actors/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknutwalker%2Ftyped-actors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknutwalker%2Ftyped-actors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknutwalker%2Ftyped-actors/lists"}