Skip to content
Bartosz Sypytkowski edited this page Dec 17, 2015 · 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" <| fun m ->
    let rec loop () = actor {
        let! msg = m.Receive ()
        match msg with
        | "stop" -> return Stop // effect
        | "unhandle" -> return Unhandled // effect
        | 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 -> unit) (context: Actor<'Message>) : Behavior<'Message> - uses a function, which takes a message as the only parameter. Context parameter is injected by spawning functions.
  • actorOf2 (fn : Actor<'Message> -> 'Message -> unit) (mailbox : Actor<'Message>) : Behavior<'Message> - uses a function, which takes both the message and an actor's context as the parameters. Context itself is injected by spawning functions.

Example usage:

let handleMessage (context: Actor<'a>) msg =
    match msg with
    | Some x -> printf "%A" x
    | None -> ()

let aref = spawn system "my-actor" (actorOf2 handleMessage)

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>) : Behavior<'Message> - an empty behavior, which will automatically ignore all incoming messages.
  • echo (context: Actor<'Message>) : Behavior<'Message> - it will resend message back to it's sender.

Example:

let blackhole = spawn system null Behaviors.ignore
let echoRef = spawn system null Behaviors.echo

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) (f : Actor<'Message> -> Behavior<'Message>) : IActorRef<'Message> - spawns an actor using specified actor computation expression. This actor can only be used locally.
  • spawnOpt (options : SpawnOption list) (actorFactory : IActorRefFactory) (name : string) (f : Actor<'Message> -> Behavior<'Message>) : IActorRef<'Message> - spawns an actor using specified actor computation expression, with custom spawn option settings. This actor can only be used locally.
  • spawne (options : SpawnOption list) (actorFactory : IActorRefFactory) (name : string) (expr : Expr<Actor<'Message> -> Behavior<'Message>>) : IActorRef<'Message> - spawns an actor using specified actor computation expression, using an F# quotations. This actor code can be deployed remotely, however it requires, that both of the nodes must know all definitions that are not part of the quotations.
  • spawnObj (actorFactory : IActorRefFactory) (name : string) (f : Quotations.Expr<unit -> #ActorBase>) : IActorRef<'Message> - spawns an actor using specified actor quotation. It can be used for actors defined in OO style.
  • spawnObjOpt (actorFactory : IActorRefFactory) (name : string) (f : Quotations.Expr<unit -> #ActorBase>) (options : SpawnOption list) : IActorRef<'Message> - spawns an actor using specified actor quotation, with custom spawn option settings. It can be used for actors defined in OO style.

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.

Spawn options

To be able to specifiy more precise actor creation behavior, you may use spawnOpt, spawnOptObj and spawne methods, both taking a list of SpawnOption values. Each specific option should be present only once in the collection. When a conflict occurs (more than one option of specified type has been found), the latest value found inside the list will be chosen.

  • SpawnOption.Deploy(Akka.Actor.Deploy) - defines deployment strategy for created actors (see: Deploy). This option may be used along with spawne function to enable remote actors deployment.
  • SpawnOption.Router(Akka.Routing.RouterConfig) - defines an actor to behave as a router and describes it's routing specifics (see: Akka routing).
  • SpawnOption.SupervisiorStrategy(Akka.Actor.SupervisiorStrategy) - defines a supervisor strategy of the current actor. It will affect it's children (see: Akka supervision).
  • SpawnOption.Dispatcher(string) - defines a type of the dispatcher used for resources management for the created actors. (See: Akka dispatchers)
  • SpawnOption.Mailbox(string) - defines a type of the mailbox used for the created actors. (See: Akka mailboxes)

Example (deploy actor remotely):

let spawnRemote addr sys name actor = 
    let options = [SpawnOption.Deploy(Deploy(RemoteScope(Address.Parse addr)))]
    spawne options sys name actor

let printer = 
    spawnRemote "akka.tcp://server@localhost:4500" client "remote-actor" 
        <@ actorOf2 (fun ctx msg -> printfn "%A received: %s" ctx.Self msg) @>