I have two actors - childActor and parentActor
open System
open Akka
open Akka.FSharp
let systemActor = System.create "systemActor" <| Configuration.defaultConfig()
let childActor (mailbox: Actor<_>) =
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "Message received"
return! loop()
}
loop()
let parentActor (mailbox: Actor<_>) =
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "Message received"
return! loop()
}
loop()
I can create the parent actor reference using spawn function.
let parentRef = spawn systemActor "parentActor" parentActor
Now what I want to do here is to create the child actor reference under parent actor. Something like below.
let childRef = spawn parentRef "childActor" childActor
But this is not working. spawn function requires a parameter of type IActorRefFactory so it is not accepting IActorRef. Is there any way to create a child actor under parent actor in akka.net using F#?
IActorRefFactory is an interface responsible for determining a parent and in case of Akka.FSharp it's implemented by ActorSystem and Actor<_> as well. So in your case just use:
let childRef = spawn mailbox "childActor" childActor
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 the following code which spawns many worker actors. The worker actor will need to send a message to the task dispatcher to ask for more tasks if idle. How the actor identify itself?
let system = System.create "System" <| Configuration.load ()
let taskDispatcher (mailbox: Actor<_>) = .... // Send message to processor to assign tasks
let processor (mailbox: Actor<MyTask>) =
let rec loop () = actor {
// ... task done. send message to the dispatcher for a new task
return! loop ()
}
loop ()
let processors = [
spawn system "System" processor
spawn system "System" processor
spawn system "System" processor
// .... many more
]
The parameter you're calling mailbox (an Actor<'t>) has a property called Self that gives you an IActorRef to the current actor. You can also get the IActorRef of the actor that sent the message you're currently processing by calling the Sender method on the mailbox. You can use these properties to reply to the sender, send messages to yourself, or to send your own actor reference to another actor so they can send you messages.
One thing to note is that you should be giving each actor a unique name when you call spawn. You can't have two actors with the same name in the same actor system.
Here's a simple example of the message loop using these properties:
type Msg = HelloFrom of IActorRef
spawn system "my-actor"
<| fun mailbox ->
let rec loop () =
actor {
let! message = mailbox.Receive()
mailbox.Sender() <! HelloFrom mailbox.Self
return! loop ()
}
loop ()
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.