如何避免对Actor实例

时间:2016-03-07 19:29:14

标签: f# immutability actor akka.net

我试图在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)方式?

2 个答案:

答案 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

现在,您不需要使用actorOf2actorOf来生成此演员,因为它已经拥有正确的签名:

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函数将非常容易。