我是F#的新手,并尝试使用MailboxProcessor进行实验,以确保状态更改是独立完成的。

简而言之,我正在将 Action (描述状态改变的不可变对象(immutable对象))发布到MailboxProcessor中,在递归函数中,我读取了消息并生成了一个新状态(即,在下面的示例中将一个项目添加到集合中)并将该状态发送给下一次递归。

open System

type AppliationState =
    {
        Store : string list
    }
    static member Default =
        {
            Store = List.empty
        }
    member this.HandleAction (action:obj) =
        match action with
        | :? string as a -> { this with Store = a :: this.Store }
        | _ -> this

type Agent<'T> = MailboxProcessor<'T>

[<AbstractClass; Sealed>]
type AppHolder private () =
    static member private Processor = Agent.Start(fun inbox ->
        let rec loop (s : AppliationState) =
            async {
                let! action = inbox.Receive()
                let s' = s.HandleAction action
                Console.WriteLine("{s: " + s.Store.Length.ToString() + " s': " + s'.Store.Length.ToString())
                return! loop s'
                }
        loop AppliationState.Default)

    static member HandleAction (action:obj) =
        AppHolder.Processor.Post action

[<EntryPoint>]
let main argv =
    AppHolder.HandleAction "a"
    AppHolder.HandleAction "b"
    AppHolder.HandleAction "c"
    AppHolder.HandleAction "d"

    Console.ReadLine()
    0 // return an integer exit code

预期输出为:
s: 0 s': 1
s: 1 s': 2
s: 2 s': 3
s: 3 s': 4

我得到的是:
s: 0 s': 1
s: 0 s': 1
s: 0 s': 1
s: 0 s': 1

阅读MailboxProcessor的文档并进行搜索,我的结论是,它是由“单线程”处理的消息队列,相反,它们似乎都是并行处理的。

我完全不在这里吗?

最佳答案

我认为问题一定出在您对HandleAction的实现中。我实现了以下内容,并产生了预期的输出。

open System

type ApplicationState =
    {
        Items: int list
    }
    static member Default = {Items = []}
    member this.HandleAction x = {this with Items = x::this.Items}

type Message = Add of int

let Processor = MailboxProcessor<Message>.Start(fun inbox ->
    let rec loop (s : ApplicationState) =
        async {
            let! (Add action) = inbox.Receive()
            let s' = s.HandleAction action
            Console.WriteLine("s: " + s.Items.Length.ToString() + " s': " + s'.Items.Length.ToString())
            return! loop s'
        }
    loop ApplicationState.Default)

Processor.Post (Add 1)
Processor.Post (Add 2)
Processor.Post (Add 3)
Processor.Post (Add 4)


// OUTPUT
// s: 0 s': 1
// s: 1 s': 2
// s: 2 s': 3
// s: 3 s': 4

编辑

看到更新的代码示例后,我相信正确的F#解决方案只是将AppHolder类型从作为类切换为模块。更新后的代码如下所示:
open System

type AppliationState =
    {
        Store : string list
    }
    static member Default =
        {
            Store = List.empty
        }
    member this.HandleAction (action:obj) =
        match action with
        | :? string as a -> { this with Store = a :: this.Store }
        | _ -> this

type Agent<'T> = MailboxProcessor<'T>

module AppHolder =
    let private processor = Agent.Start(fun inbox ->
        let rec loop (s : AppliationState) =
            async {
                let! action = inbox.Receive()
                let s' = s.HandleAction action
                Console.WriteLine("{s: " + s.Store.Length.ToString() + " s': " + s'.Store.Length.ToString())
                return! loop s'
            }
        loop AppliationState.Default)

    let handleAction (action:obj) =
        processor.Post action


AppHolder.handleAction "a"
AppHolder.handleAction "b"
AppHolder.handleAction "c"
AppHolder.handleAction "d"

这将输出与以前相同的结果:
{s: 0 s': 1
{s: 1 s': 2
{s: 2 s': 3
{s: 3 s': 4

10-08 01:52