In C# ReceiveActor
s I can just have state as private fields in the class. How should I do this in an idiomatic way with the F# API?
Is this a good idea? Any alternatives?
let handleMessage (mailbox: Actor<'a>) msg =
let mutable i = 1
match msg with
| Some x -> i <- i + x
| None -> ()
The way you've proposed is entirely appropriate as a means of storing the state within the actor. The concurrency constraints of only processing 1 message at any time means that it's not possible to get into invalid states as a result of contention on a shared memory location.
However, it's not the most idiomatic option. Akka.Net provides an F# API to work with actors in a similar way to F# MailboxProcessors. In this case you define your actor as a tail recursive function which calls itself with some new state. Here's an example
spawn system "hello" <|
fun mailbox ->
let rec loop state =
actor {
let! msg = mailbox.Receive ()
printfn "Received %A. Now received %s messages" msg state
return! loop (state + 1) //Increment a counter for the number of times the actor has received a message
}
loop 0
For full documentation on the Akka.Net F# API see http://getakka.net/wiki/FSharp%20API
There are two solutions, both of them use explicit recursive loop definition, main concept of Akka F# actors.
First you may define variables, which should be visible only inside actor's scope, before loop definition (in example below I've changed i
definition to reference cell, because mutable variables cannot be captured by closures):
let actorRef =
spawn system "my-actor" <| fun mailbox ->
let i = ref 1
let rec loop () =
actor {
let! msg = mailbox.Receive()
match msg with
| Some x -> i := !i + x
| None -> ()
return! loop()
}
loop()
However, more advised solution is to keep your state immutable during message handling, and change it only when passing in next loop calls, just like this:
let actorRef =
spawn system "my-actor" <| fun mailbox ->
let rec loop i =
actor {
let! msg = mailbox.Receive()
match msg with
| Some x -> return! loop (i + x)
| None -> return! loop i
}
loop 1 // invoke first call with initial state