F# 如何避免对Actor实例的可变引用

F# 如何避免对Actor实例的可变引用,f#,immutability,actor,akka.net,F#,Immutability,Actor,Akka.net,我正试图在F#中获得一些与Akka.NET演员合作的经验。我有这样一个场景,一个演员需要把另一个演员演成他的孩子。第一个参与者将转换每条消息,然后将结果发送给另一个参与者。我使用actorOf2函数生成演员。这是我的密码: let actor1 work1 work2 = let mutable actor2Ref = null let imp (mailbox : Actor<'a>) msg = let result = work1 msg if acto

我正试图在F#中获得一些与Akka.NET演员合作的经验。我有这样一个场景,一个演员需要把另一个演员演成他的孩子。第一个参与者将转换每条消息,然后将结果发送给另一个参与者。我使用
actorOf2
函数生成演员。这是我的密码:

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"
让actor1工作1工作2=
设可变actor2Ref=null
让imp(mailbox:Actormailbox.Context
生成一个子角色,在第一次调用之前我没有任何上下文。我看到了,但我不知道如何将其应用于我的函数

在一个更高级的场景中,我需要一个由键划分的子参与者集合。在这个场景中,我使用的是参与者引用字典。是否有更好的(更富F#ish)方法?

为了在迭代过程中保持“状态”,您需要显式地进行迭代。这样,您可以传递当前的“状态”作为尾部调用参数。正如您链接的问题中所述:

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
):

让ActorofithState(f:Actor'msg->'状态)(initialState:'状态)邮箱=
让rec imp state=
演员{
let!msg=mailbox.Receive()
让newState=f邮箱状态msg
返回!imp newState
}
imp初始状态
然后:

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"

让actor1工作1工作2(mailbox:Actor您可以按照这些思路做一些事情,而不存储对子Actor的引用,因为上下文已经在为您做这些了

let actor =
    let ar = mailbox.Context.Child(actorName)
    if ar.IsNobody() then
        spawn mailbox.Context actorName handler
    else ar

如果Context.Child查找速度太慢,那么创建一个记忆化函数,使可变性不被其他代码发现将非常容易。

是的,我想到了这个选项,但是与我的函数相比,这个带有显式接收和参与者工作流的模板更像样板。而且更难进行单元测试。是的,有些更多的样板文件,但没有它,您就无法维护状态。想想看:如果您的函数不接受任何表示状态的参数,那么它所能做的就是依赖可变环境。我已经更新了答案,以回应您的担忧。
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"
let actor =
    let ar = mailbox.Context.Child(actorName)
    if ar.IsNobody() then
        spawn mailbox.Context actorName handler
    else ar