Cricket


Hello World Example

The example below is essentially the Hello World of actors. It is a simple actor called greeter which responds to messages of type Say. Given a message the actor will write a response to the debug log then wait for another message.

ActorHost.Start()

Before we create any actor we must start the actor host for out actor to live in. There is only one actor host per process.

Next we create a type to represent the messages that we want to send. In this example we create a three legged union to represent our messages, but this could just of easily been a class, record or any other type really. Typically though unions are a good fit for defining message types.

type Say =
    | Hello
    | HelloWorld
    | Name of string

Now we have our message type, its is time to create our actor. To do this we can use the actor {} syntax. The syntax allows for various different properties to be set (see here), but name and messageHandler are the minimum required to get a functioning actor. The messageHandler is simply a function that provides the behaviour of the actor. It is of type ActorCell<'a> -> Async<unit> where the ActorCell<'a> type holds the context for this actor. To wait for a message we simply call receive, this will block this thread until a message arrives at the actor. Once it arrives, the function will resume and process the message. Once we have finished defininf our actor configuration we can spawn the actor.

let greeter = 
    actor {
        name "greeter"
        body (
            let rec loop() = messageHandler {
                let! msg = Message.receive() //Wait for a message

                match msg with
                | Hello ->  printfn "Hello" //Handle Hello leg
                | HelloWorld -> printfn "Hello World" //Handle HelloWorld leg
                | Name name -> printfn "Hello, %s" name //Handle Name leg

                return! loop() //Recursively loop

            }
            loop())
    } |> Actor.spawn

Once the actor is spawned it is now ready to except messages. To post a message to an actor we can use the <-- operator, like this.

greeter <-- Name("from F# Actor")

This requires you to have a direct reference to the actor, which is not always practical. It would be nice to be able to resolve actors by name at runtime and in fact the framework lets you do this using the !! operator.

let resolvedGreeter = !!"greeter"

The !! operator returns an actorSelection type, which can represent one or many actors that match the path you gave (for more on actor lookup see here). once we have an actorSelection we can use it exactly like the direct reference.

resolvedGreeter <-- Hello

Or we can inline all of this

"greeter" <-- Hello

System Messages

Great.. so at this point we have an actor and can send messages to it. But we have no obvious way of shutting down this actor once we are done with it. Now we could add an extra leg to our message type Stop for example that would cause the message loop to not recursively return but instead simply exit. And in some domains this will be desirable. But it is worth noting that the F# Actor Framework, provides a set of System Messages that allow you to control certain framework level items to do with your actor, like shutting down, restarting or several other tasks to do with supervisors.

Here we will just focus on shutting down the actor, since this is built into the framework all we have to do is send it the Shutdown or Restart message

greeter <-- Restart

greeter <-- Shutdown

Thats about it for the basics, for more indepth examples have a look at the links below.

More in depth examples

  • Ping Pong - A slightly more advanced example, showing how two actors can communicate with each other within the same process.
  • Ping Pong - Remoting Version - Same as the in process Ping Pong example, however the actors are in seperate processes.
namespace Cricket
type ActorHost =
  interface IDisposable
  private new : configuration:ActorHostConfiguration -> ActorHost
  member private Configure : f:(ActorHostConfiguration -> ActorHostConfiguration) -> unit
  member private RegisterActor : ref:ActorRef -> unit
  member private ResolveActor : name:ActorPath -> ActorRef list
  member SubscribeEvents : eventF:('a0 -> unit) -> ActorHost
  member private CancelToken : CancellationToken
  member private EventStream : IEventStream
  member private Name : string
  static member Dispose : unit -> unit
  ...

Full name: Cricket.ActorHost
static member ActorHost.Start : ?name:string * ?loggers:ILogWriter list * ?serializer:'a0 * ?registry:ActorRegistry * ?metrics:Diagnostics.MetricsConfiguration * ?tracing:Diagnostics.TracingConfiguration * ?cancellationToken:System.Threading.CancellationToken -> ActorHost
type Say =
  | Hello
  | HelloWorld
  | Name of string

Full name: Tutorial.Say
union case Say.Hello: Say
union case Say.HelloWorld: Say
union case Say.Name: string -> Say
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val greeter : ActorRef

Full name: Tutorial.greeter
val actor : ActorConfigurationBuilder

Full name: Cricket.ActorConfiguration.actor
custom operation: name (string)

Calls ActorConfigurationBuilder.Name
custom operation: body (MessageHandler<ActorCell<'a>,unit>)

Calls ActorConfigurationBuilder.Body
val loop : (unit -> MessageHandler<ActorCell<Say>,'a>)
val messageHandler : Message.MessageHandlerBuilder

Full name: Cricket.ActorConfiguration.messageHandler
val msg : Say
Multiple items
module Message

from Cricket

--------------------
type Message<'a> =
  {Id: uint64 option;
   Sender: ActorRef;
   Message: 'a;}

Full name: Cricket.Message<_>
val receive : unit -> MessageHandler<ActorCell<'a>,'a>

Full name: Cricket.Message.receive
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val name : string
Multiple items
module Actor

from Cricket

--------------------
type Actor<'a> =
  interface IDisposable
  interface IActor<'a>
  interface IActor
  new : defn:ActorConfiguration<'a> -> Actor<'a>
  override ToString : unit -> string
  member Ref : ActorRef

Full name: Cricket.Actor<_>

--------------------
new : defn:ActorConfiguration<'a> -> Actor<'a>
val spawn : config:ActorConfiguration<'a> -> ActorRef

Full name: Cricket.Actor.spawn
val resolvedGreeter : ActorSelection

Full name: Tutorial.resolvedGreeter
union case SystemMessage.Restart: SystemMessage
union case SystemMessage.Shutdown: SystemMessage
Fork me on GitHub