F# Actor: An Actor Library

Wikipedia, defines an actor as –

An actor is a computational entity that, in response to a message it receives, can concurrently:

  • send a finite number of messages to other actors;
  • create a finite number of new actors;
  • designate the behavior to be used for the next message it receives.

Recently there has been some discussion in the F# open source community about an Akka like actor framework for F#. The following post introduces a very early version of the framework, in the hope that it will get some community interest and contributions.

Creating actors and sending messages

To create an actor we need to simply provide a name and a function that takes an actor instance and returns a unit Async computation

let multiplication = 
    (fun (actor:Actor<_>)  ->
        async {
            let! (a,b) = actor.Receive()
            let result = a * b
            do printfn "%A: %d * %d = %d" actor.Path a b result
        }
    )

let addition = 
    (fun (actor:Actor<_>) ->
        async {
            let! (a,b) = actor.Receive()
            let result = a + b
            do printfn "%A: %d + %d = %d" actor.Path a b result
        }
    )

let calculator = 
    [
       Actor.spawn (ActorPath.create "calculator/addition") addition
       Actor.spawn (ActorPath.create "calculator/multiplication") multiplication
    ]

Here we have created two actors, one that carries out multiplication and one that carries out addition. To send a message to the actors we can simply get the instance and call the post method.

calculator.[0].Post((5,2))

However in an actor framework having to directly reference the actors to call post on them can be some what limiting. So when each actor is created it is assigned a path. The path is created with the following schema (actor://{machinename}/{path}). We can use this path to resolve the actor,

"actor://main-pc/calculator/addition" ?<-- (5,2)

If we know that this actor is local then we do not need to qualify the path.

"calculator/addition" ?<-- (5,2)

In addition to resolving single actors we can also broad cast to several actors at once by providing a partial path,

"calculator/" ?<-- (5,2)

Sending Messages to Remote Actors

Messages are sent via transports. Out of the box, the framework provides a TCP transport based on the high performance Fracture I/O library. Before you can send remote messages you must register a transport.

let transport = new FractureTransport({ Port = Some 8080}) :> ITransport
Remoting.registerTransport Serialisers.Binary transport

When a transport is created it is wrapped in a actor of Actor and path ‘transports/{scheme}’ in the case of the fracture transport this would be ‘transports/actor.fracture’. This actor is then supervised (see below) by the system/remotingsupervisor actor, which is initialised the a OneForOne strategy so will restart the transport if it errors at any point. Once a transport is registered we can then send a message to a remote actor by using a fully qualified actor path, for example,

"actor.fracture://10.256.76.94:8080/calculator/addition" ?<-- (5,2)

notice here that the scheme selects which transport the message will be sent on. If you wanted to send a message on another registered transport (http for example), the path would be,

"actor.http://10.256.76.94:8082/calculator/addition" ?<-- (5,2)

Supervisors

Supervisors, provide a way to monitor make decisions about what to do when a actor errors. How the supervisor responds when an actor receives an error depends on the strategy provided (for more info see here.

To create a supervised actor, we can simply do the follwoing

let err = 
    (fun (actor:Actor<string>) ->
        async {
            let! msg = actor.Receive()
            if msg <> "fail"
            then printfn "%s" msg
            else failwithf "ERRRROROROR"
        }
    ) |> Actor.spawn (ActorPath.create "err_0")

let supervisor =  Actor.supervisor (ActorPath.create "oneforone") Supervisor.OneForOne [err]

if we now post ‘fail’ to this actor it will throw an exception and the supervisor will step in and restart the actor.

Conclusion

This has just been a brief introduction into the framework, there is much more in the pipeline the project is in its very early stages. I welcome anyone who wants to contribute and shape this into a fully fledged actor framework. So basically rip it apart tell let me know what you like don’t like, send pull requests raise bugs, feature requests, improve documentation. Any contribution will be welcomed.

The source for this can be found on GitHub here. And more complete documentation here

Advertisements
This entry was posted in Development and tagged , . Bookmark the permalink.

3 Responses to F# Actor: An Actor Library

  1. Pingback: F# Weekly #21 2013 | Sergey Tihon's Blog

  2. Pingback: F# Actor: An Actor Library | Disparate Opinions

  3. Pingback: The Actor Model in F# – Jayway

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s