我试图在F#中获得Akka.NET演员的一些经验。我有一个场景,当一个演员需要将另一个演员作为其孩子。第一个actor将转换每个消息,然后将结果发送给另一个actor。我使用actorOf2
函数来生成actor。这是我的代码:
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"
我不喜欢的是可变的演员参考。但是我必须让它变得可变,因为我需要mailbox.Context
来产生一个儿童演员,而且在第一次调用之前我没有任何上下文。我看到了this question,但我不知道如何将它应用到我的功能中。
在更高级的场景中,我需要一个由键分隔的子actor的集合。在这种情况下,我使用了actor的词典。是否有更好的(更多F#-ish)方式?
答案 0 :(得分:3)
为了保持你的状态&#34;在迭代中,您需要使迭代显式化。这样,你就可以传递当前的状态&#34;作为尾调用参数。就像你链接的问题一样:
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
现在,您不需要使用actorOf2
或actorOf
来生成此演员,因为它已经拥有正确的签名:
let actor1Ref =
actor1 work1' work2'
|> spawn system "my-actor"
。
的修改
如果你担心额外的样板,没有什么可以阻止你将样板包装成一个函数(毕竟,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
然后:
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"
答案 1 :(得分:2)
你可以沿着这些方面做点什么,而根本不存储对子actor的引用,因为Context已经为你做了这些。
let actor =
let ar = mailbox.Context.Child(actorName)
if ar.IsNobody() then
spawn mailbox.Context actorName handler
else ar
如果Context.Child查找结果太慢,那么创建一个让其他代码隐藏可变性的memoized函数将非常容易。