Akka.NET F# API 和具有堆叠行为的 actor

Akka.NET F# API and actors with stacked behavior

Akka 及其 Akka.NET 端口支持可切换的行为 (http://getakka.net/docs/working-with-actors/Switchable%20Behaviors),并且不仅仅是让一个 actor 成为具有不同行为的 actor 的简单形式,而且还以一种行为形式stack:一个actor可以回到它之前的行为。这是通过 BecomeStackedBecomeUnstacked.

方法实现的

在 F# 中以不同的方式支持行为:不需要 Become 方法,而是一个 actor 函数可以定义多个邮箱处理处理程序并调用不同的处理程序有效切换一个演员不同的行为。这适用于非堆叠行为,但如果一个 actor 函数需要堆叠其行为怎么办?这如何在 F# 中实现?

更新。用用例来说明。考虑参与者可以具有行为 DirectCall、ConferenceCall 和 OnHold 的电话。 OnHold 行为可从 DirectCall 和 ConferenceCall 获得,并且在 OnHold 期间,参与者可以接收消息以启动嵌套呼叫。嵌套呼叫完成后,参与者将返回到之前的呼叫,该呼叫可以是 DirectCall 或 ConferenceCall。如果不显式地将完整的堆栈发送给参与者,我看不出这是如何实现的。毕竟,还不错,我只是想知道 Akka.NET 是否提供了另一种方法。邮箱 Context.BecomeXXX 在 F# API 中仍然可用,但它在 "actor" 计算表达式中没有用处。

更新 2 我必须同意 Tomas Jansson 的观点,他在评论中推理说他宁愿明确地发送以前的行为堆栈(或者说以前的状态)而不是依靠 Akka 的内置 BecomeStacked 方法隐藏历史并使其更难测试。因此,F# 中缺少 BecomeXXX 方法 API 并没有真正削弱它的功能。

我写了一个简单的示例,说明如何使用 MailboxProcessor 解决它,概念是相同的,因此您应该能够轻松地将其转换为 akka.net:

type Message = 
    | Ping
    | Back

type State = {Behavior: State -> Message -> State; Previous: State -> Message -> State}

let rec pong state message =
    match message with 
    | Ping -> 
        printfn "Pong"
        {state with Behavior = superpong} 
    | Back ->
        {state with Behavior = state.Previous}
and superpong state message = 
    match message with 
    | Ping ->
        printfn "Super pong"
        {state with Behavior = pong} 
    | Back ->
        {state with Behavior = state.Previous}
        
let agent = new MailboxProcessor<Message>(fun inbox ->
    let setPrevious oldState newState = 
        {newState with Previous = oldState.Behavior}
    let rec loop state = 
        async {
            let! msg = inbox.Receive()
            return! loop (msg |> state.Behavior state |> setPrevious state)
        }
    loop {Behavior = pong; Previous = pong})
    
agent.Start()
agent.Post(Ping)
agent.Post(Ping)
agent.Post(Back)
agent.Post(Ping)
agent.Post(Ping)

输出为:

Pong

Super pong

Super pong

Pong

想法是您将行为作为代理状态的一部分。请注意,函数必须使用 let rec ... and ... 语法定义,以便第二个函数可以 return 处于新状态的第一个函数。

更新 简化了只有一种消息类型的示例。