Skip to content
Riccardo Terrell edited this page Feb 4, 2019 · 8 revisions

Unlike C# actors, which represent object oriented nature of the language, F# is able to define an actor's logic in more functional way. It's done by using actor computation expression. In most cases, an expression inside an actor is expected to be represented as self-invoking recursive function - using mutually recursive functions in this context is allowed and even desired, i.e. to create more advanced constructs like Finite State Machines.

Each actor returning point should point to either recursive loop call or return an Effect.

Example:

let helloRef = spawn system "hello-actor" <| props(fun context ->
    let rec loop () = actor {
        let! msg = context.Receive ()
        match msg with
        | "stop" -> return Stop // effect - stops current actor
        | "unhandle" -> return Unhandled // effect - marks message as unhandled
        | x -> 
            printfn "%s" x
            return! loop ()
    }
    loop ())

Since construct used in an example above is quite popular, you may also use following shorthand functions to define message handler's behavior:

  • actorOf (fn : 'Message -> #Effect<'Message>) (mailbox : Actor<'Message>) : Effect<'Message> - uses a function, which takes a message as the only parameter. Context parameter is injected by spawning functions.
  • actorOf2 (fn : Actor<'Message> -> 'Message -> #Effect<'Message>) (mailbox : Actor<'Message>) : Effect<'Message> - uses a function, which takes both the message and an actor's context as the parameters. Context itself is injected by a spawning functions.

Example usage:

let rec handleMessage state (context: Actor<'a>) = function
    | "print" -> printf "%A" state |> ignored           // returned effect - stay the same
    |  x -> become (handleMessage (x::state) context)   // returned effect - update the actor's state

let aref = spawn system "my-actor" (props (actorOf2 (handleMessage []))) // init actor state with empty list

Behaviors

Since some of the functional behaviors are quite popular, you can find them inside Akkling.Behaviors module. Currently their list consist of:

  • ignore (context:Actor<'Message>) : Effect<'Message> - an empty behavior, which will automatically ignore all incoming messages.
  • echo (context: Actor<'Message>) : Effect<'Message> - it will resend message back to it's sender.
  • printf (fmt: Printf.TextWriterFormat<'Message->unit>) (context: Actor<'Message>) : Effect<'Message> - takes format string and print's each incoming message using that format.

Example:

let blackhole = spawnAnonymous system (props Behaviors.ignore)
let echoRef = spawnAnonymous system (props Behaviors.echo)

Props

Props<'Message> is a typed descriptor containing all information necessary to create particular actor type. There are many different props methods, depending on additional features of actors (like support for eventsourcing). By default props and propse are two functions used to produce props from actor functions. Difference between them is that props takes simple actor behavior function, while propse takes a quotation which can be serialized and compiled on the other side.

Spawning actors

Paragraph above already has shown, how actors may be created with help of the spawning function. There are several spawning function, which may be used to instantiate actors:

  • spawn (actorFactory : IActorRefFactory) (name : string) (p: Props<'Message>) : IActorRef<'Message> - spawns an actor using specified actor computation expression.
  • spawnAnonymous (actorFactory : IActorRefFactory) (p: Props<'Message>) : IActorRef<'Message> - spawns an actor without need of providing actor's name.

All of these functions may be used with either actor system or actor context. In the first case spawned actor will be placed under /user root guardian of the current actor system hierarchy. In second option spawned actor will become child of the actor used as [actorFactory] parameter of the spawning function.