Issue:
I am struggling to understand why my Reporter actor is not receiving messages based on the following statement that's in my Generator actor:
reporter <! Message input
My reporter actor is the following:
let reporterActor (mailbox:Actor<_>) =
let rec loop() = actor { let! msg = mailbox.Receive()
match msg |> box :?> Command with
| Start -> ()
| Message v -> printf "%s" v
| Exit -> mailbox.Context.System.Terminate() |> ignore }
loop() |> ignore
Basically, a console is launched that accepts input from the user. My Generator actor forwards that input to my Reporter actor. However, the code above never gets executed.
The code is the following:
module Main
open System
open Akka.FSharp
open Akka.Actor
open Actors
type Command =
| Message of string
| Start | Exit
let reporterActor (mailbox:Actor<_>) =
let rec loop() = actor { let! msg = mailbox.Receive()
match msg |> box :?> Command with
| Start -> ()
| Message v -> printf "%s" v
| Exit -> mailbox.Context.System.Terminate() |> ignore }
loop() |> ignore
let generatorActor (reporter:IActorRef) (mailbox:Actor<_>) message =
let handle input = match input with
| "exit" -> mailbox.Context.System.Terminate |> ignore
| _ -> reporter <! Message input
handle (Console.ReadLine().ToLower())
[<EntryPoint>]
let main argv =
let system = System.create "system" (Configuration.load())
let reporterActor = spawn system "reporterActor" (actorOf(reporterActor))
let generatorActor = spawn system "generatorActor" (actorOf2(generatorActor reporterActor))
generatorActor <! Start
system.AwaitTermination ()
0
Update:
I learned that I could trigger my Reporter actor by replacing the mailbox parameter with an arbitrary message parameter:
let reporterActor message =
match message |> box :?> Command with
| Start -> ()
| Message v -> printf "Reporting: %s" v
| Exit -> failwith "Kill this!"
I still don't understand when I should use a mailbox parameter versus when I should rely on a message parameter.
The difference is in how actorOf and actorOf2 work.
actorOf in conjunction with spawn creates an actor as a child of the root of the system which will handle messages with the function 'Message -> unit that was passed to it.
actorOf2 in in conjunction with spawn creates an actor as a child of the actor that you passed in and the child will handle the messages with the function 'Message -> unit that was passed.
Your original function signature for reporter actor was:
Actor<'Message> -> unit
and you used spawn system "reporterActor" (actorOf(reporterActor))
In this case you were saying that the message type that the new actor that was created would receive would be of type Actor<'Message> . This compiled because actorof just expects a function that takes a 'Message, and a 'Message is generic therefore Actor<'Message> satisfied the 'Message parameter.
When you updated the signature of reporterActor you change the signature to 'Message -> unit which is what actorOf is actually intended to accept.
In short generics allowed your code to compile since 'Message isn't really restricted, nor should it really be.
From: http://getakka.net/docs/FSharp%20API
actorOf (fn : 'Message -> unit) (mailbox : Actor<'Message>) :
Cont<'Message, 'Returned> - uses a function, which takes a message as
the only parameter. Mailbox parameter is injected by spawning
functions.
actorOf2 (fn : Actor<'Message> -> 'Message -> unit) (mailbox :
Actor<'Message>) : Cont<'Message, 'Returned> - uses a function, which
takes both the message and an Actor instance as the parameters.
Mailbox parameter is injected by spawning functions. Example:
> let handleMessage (mailbox: Actor<'a>) msg =
> match msg with
> | Some x -> printf "%A" x
> | None -> ()
>
> let aref = spawn system "my-actor" (actorOf2 handleMessage) let
> blackHole = spawn system "black-hole" (actorOf (fun msg -> ()))
spawn (actorFactory : IActorRefFactory) (name : string) (f :
Actor<'Message> -> Cont<'Message, 'Returned>) : IActorRef - spawns an
actor using a specified actor computation expression. The actor can
only be used locally.
All of these functions may be used with either the actor system or the
actor itself. In the first case the spawned actor will be placed under
/user root guardian of the current actor system hierarchy. In the
second option the spawned actor will become a child of the actor used
as the actorFactory parameter of the spawning function.
Related
I created the following test code - .Net core 2.1 console application. It prints the following message only
TestActor received message MyTask ("Test1","Test1") from [akka://MySystem/user/Scheduler#1426101451]
But the message Ok 0 cannot be received by the actor scheduler?
open System
open Akka.FSharp
open Akka
type MyTask = MyTask of item1: string * item2: string
let system = System.create "MySystem" <| Configuration.load ()
let scheduler (actors: Actor.IActorRef) (mailbox: Actor<Result<int, string>>) =
let rec loop (list: int list list) = actor {
let! m = mailbox.Receive ()
let sender = mailbox.Sender ()
let akkaName = mailbox.Self.Path.Name
printfn "%s received message %A from %A" akkaName m sender
return! loop []
}
actors <! MyTask("Test1", "Test1")
loop []
let processor (mailbox: Actor<MyTask>) =
let rec loop () = actor {
let! m = mailbox.Receive ()
let sender = mailbox.Sender ()
let akkaName = mailbox.Self.Path.Name
printfn "%s received message %A from %A" akkaName m sender
sender <! Ok 0 // scheduler cannot receive this message?
return! loop ()
}
loop ()
[<EntryPoint>]
let main argv =
let actor = spawn system "TestActor" processor
spawn system "Scheduler" (scheduler actor) |> ignore
system.WhenTerminated.Wait()
0
Update:
It works after I changed the parameter from (mailbox: Actor<Result<int, string>>) to (mailbox: Actor<_>)?
The scheduler isn't the sender of the MyTask message to which the processor is replying, because you are doing the tell (<!) from outside the actor computation. That means it's basically being sent with no sender. You can use the Tell method on IActorRef to send with an explicit sender, since you want to send it from outside the context of your actor computation:
actors.Tell(MyTask("Test1", "Test1"), mailbox.Self)
EDIT
Another issue is that the mailbox parameter to the scheduler is typed as Actor<Result<int, string>>, but since Ok 0 will be inferred as Result<int,obj> in your context, it won't match the actor type signature, and the message will be ignored. When defining an actor with a specific message type, any messages of a different type will be ignored.
I have tried following code to view/capture dead letters but it is not working in a way it should. What I am missing.exactly? My aim is just to view all the dead letters that are being delivered to deadletters actor.
let system = ActorSystem.Create("FSharp")
let echoServer =
spawn system "EchoServer"
<| fun mailbox ->
let rec loop() =
actor {
let! message = mailbox.Receive()
match box message with
| :? string ->
printfn "Echo '%s'" message
return! loop()
| _ -> failwith "unknown message"
}
loop()
let boolval = system.EventStream.Subscribe(echoServer,typedefof<DeadLetterActorRef>)
echoServer.Tell("First Message")
echoServer.Tell("Second Message")
system.DeadLetters.Tell("Dead Message")
When you subscribe to the event bus, you're subscribing to the type of message published to the bus. In the code you posted you registered the subscriber to the DeadLetterActorRef message, whereas dead letters are published in the form of DeadLetter messages. So in your case, you just need to change your subscription to
let boolval = system.EventStream.Subscribe(echoServer, typeof<DeadLetter>)
I'm trying to gain some experience with Akka.NET actors in F#. I have the scenario when one actor needs to spin up another actor as its child. The first actor would convert each message and then send the result to the other actor. I use actorOf2 function to spawn actors. Here is my code:
let actor1 work1 work2 =
let mutable actor2Ref = null
let imp (mailbox : Actor<'a>) msg =
let result = work1 msg
if actor2Ref = null then
actor2Ref <- spawn mailbox.Context "decide-actor" (actorOf2 <| work2)
actor2Ref <! result
imp
let actor1Ref = actor1 work1' work2'
|> actorOf2
|> spawn system "my-actor"
What I don't like is the mutable actor reference. But I had to make it mutable because I need mailbox.Context to spawn a child actor, and I don't have any context before the first call. I saw this question but I don't know how to apply it to my function.
In a more advanced scenario I need a collection of child actors which is partitioned by a key. I'm using a Dictionary of actor refs in this scenario. Is there a better (more F#-ish) way?
In order to keep your "state" across iterations, you need to make the iterations explicit. That way, you can pass the current "state" as tail call argument. Just as in the question you linked:
let actor1 work1 work2 (mailbox : Actor<'a>) =
let rec imp actor2 =
actor {
let! msg = mailbox.Receive()
let result = work1 msg
let actor2 =
match actor2 with
| Some a -> a // Already spawned on a previous iteration
| None -> spawn mailbox.Context "decide-actor" (actorOf2 <| work2)
actor2 <! result
return! imp (Some actor2)
}
imp None
And now, you don't need to use actorOf2 or actorOf for spawning this actor, because it already has the right signature:
let actor1Ref =
actor1 work1' work2'
|> spawn system "my-actor"
.
EDIT
If you're concerned about the extra boilerplate, nothing prevents you from packing the boilerplate away as a function (after all, actorOf2 does something similar):
let actorOfWithState (f: Actor<'msg> -> 'state -> 'msg -> 'state) (initialState: 'state) mailbox =
let rec imp state =
actor {
let! msg = mailbox.Receive()
let newState = f mailbox state msg
return! imp newState
}
imp initialState
And then:
let actor1 work1 work2 (mailbox : Actor<'a>) actor2 msg =
let result = work1 msg
let actor2 =
match actor2 with
| Some a -> a
| None -> spawn mailbox.Context "decide-actor" (actorOf2 work2)
actor2 <! result
actor2
let actor1Ref =
actor1 work1' work2'
|> actorOfWithState
|> spawn system "my-actor"
You could do something along these lines, and just not store a reference to the child actor at all, because the Context is already doing that for you.
let actor =
let ar = mailbox.Context.Child(actorName)
if ar.IsNobody() then
spawn mailbox.Context actorName handler
else ar
If the Context.Child lookup turns out to be too slow, creating a memoized function that keeps mutability hidden from the other code would be pretty easy to do.
Please see last edit.
Apologies for the newbie question. I am trying to implement something in F# using Akka.net. I'm very new to F# and I have only used Akka from Scala. Basically I am trying to implement something that's pretty easy in Scala, namely making an Actor do different things based on the type of message it receives.
My code is below and it's a slight modification of the hello world example lifted from the akka.net website. I believe a first problem with my code is that it does record pattern matching instead of type pattern matching, however I was unable to write a type match one without compilation errors... Any help will be greatly appreciated. Thank you.
open Akka.FSharp
open Actors
open Akka
open Akka.Actor
type Entries = { Entries: List<string>}
let system = ActorSystem.Create "MySystem"
let feedBrowser = spawn system "feedBrowser" <| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
match msg with
| { Entries = entries} -> printf "%A" entries
| _ -> printf "unmatched message %A" msg
return! loop()}
loop()
[<EntryPoint>]
let main argv =
feedBrowser <! "abc" // this should not blow up but it does
system.AwaitTermination()
0
Edit: the error is a runtime one, System.InvalidCastException, unable to cast object of type String to Entries.
Later edit: I got this to work with this change, downcasting to Object:
let feedBrowser = spawn system "feedBrowser" <| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
let msgObj = msg :> Object
match msgObj with
| :? Entries as e -> printfn "matched message %A" e
| _ -> printf "unmatched message %A" msg
return! loop()}
loop()
Now these two lines work correctly
feedBrowser <! "abc"
feedBrowser <! { Entries = ["a"; "b"] }
the first one prints "unmatched message abc" and the second outputs the entries.
Is there a better way of going about this, without the cast? Does akka.net have something specifically for this case?
Thank you.
You should use a Discriminated Union (the Command type in this example). Then you can pattern match its options.
type Entries = { Entries: List<string>}
type Command =
| ListEntries of Entries
| OtherCommand of string
let stack() =
let system = ActorSystem.Create "MySystem"
let feedBrowser = spawn system "feedBrowser" <| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
match msg with
| ListEntries { Entries = entries} -> printf "%A" entries
| OtherCommand s -> printf "%s" s
return! loop() }
loop()
And to send the message you should use:
feedBrowser <! OtherCommand "abc"
feedBrowser <! ListEntries { Entries = ["a"; "b"] }
It's important to say that the send operator has the following signature:
#ICanTell -> obj -> unit
So, if you pass an message with a different type, like a string, it'll raise an exception.
I can't work out why the following code is hanging at the call to GetTotal. I don't seem to be able to debug inside the MailboxProcessor, so it's hard to see what's going on.
module Aggregator
open System
type Message<'T, 'TState> =
| Aggregate of 'T
| GetTotal of AsyncReplyChannel<'TState>
type Aggregator<'T, 'TState>(initialState, f) =
let myAgent = new MailboxProcessor<Message<'T, 'TState>>(fun inbox ->
let rec loop agg =
async {
let! message = inbox.Receive()
match message with
| Aggregate x -> return! loop (f agg x)
| GetTotal replyChannel ->
replyChannel.Reply(agg)
return! loop agg
}
loop initialState
)
member m.Aggregate x = myAgent.Post(Aggregate(x))
member m.GetTotal = myAgent.PostAndReply(fun replyChannel -> GetTotal(replyChannel))
let myAggregator = new Aggregator<int, int>(0, (+))
myAggregator.Aggregate(3)
myAggregator.Aggregate(4)
myAggregator.Aggregate(5)
let totalSoFar = myAggregator.GetTotal
printfn "%d" totalSoFar
Console.ReadLine() |> ignore
It seems to work fine when using an identical MailboxProcessor directly, rather than wrapping in the Aggregator class.
The problem is that you did not start the agent. You can either call Start after you create the agent:
let myAgent = (...)
do myAgent.Start()
Alternatively, you can create the agent using MailboxProcessor<'T>.Start instead of calling the constructor (I usually prefer this option, because it looks more functional):
let myAgent = MailboxProcessor<Message<'T, 'TState>>.Start(fun inbox -> (...) )
I suppose that you couldn't debug the agent, because the code inside agent wasn't actually running. I tried adding printfn "Msg: %A" message right after the call to Receive inside the agent (to print incoming messages for debugging) and I noticed that, after calling Aggregate, no messages were actually received by the agent... (It only blocked after calling GetTotal, which avaits reply)
As a side-note, I would probably turn GetTotal into a method, so you'd call GetTotal(). Properties are re-evaluated each time you access them, so your code does the same thing, but best practices don't recommend using properties that do complex work.
You forgot to start the mailbox:
open System
type Message<'T, 'TState> =
| Aggregate of 'T
| GetTotal of AsyncReplyChannel<'TState>
type Aggregator<'T, 'TState>(initialState, f) =
let myAgent = new MailboxProcessor<Message<'T, 'TState>>(fun inbox ->
let rec loop agg =
async {
let! message = inbox.Receive()
match message with
| Aggregate x -> return! loop (f agg x)
| GetTotal replyChannel ->
replyChannel.Reply(agg)
return! loop agg
}
loop initialState
)
member m.Aggregate x = myAgent.Post(Aggregate(x))
member m.GetTotal = myAgent.PostAndReply(fun replyChannel -> GetTotal(replyChannel))
member m.Start() = myAgent.Start()
let myAggregator = new Aggregator<int, int>(0, (+))
myAggregator.Start()
myAggregator.Aggregate(3)
myAggregator.Aggregate(4)
myAggregator.Aggregate(5)
let totalSoFar = myAggregator.GetTotal
printfn "%d" totalSoFar
Console.ReadLine() |> ignore