Невозможно сохранить состояние с процессором почтовых ящиков (F#)

Я пытаюсь создать список строк, в который элементы постепенно вставляются асинхронно с помощью процессора почтового ящика. Однако я не получаю желаемого результата.

Я в значительной степени следовал коду из https://fsharpforfunandprofit.com/posts/concurrency-actor-model/ однако в моем случае это не работает должным образом. Код у меня следующий:

type TransactionQueue ={

queue : string list

} with

static member UpdateState (msg : string) (tq : TransactionQueue) =
    {tq with queue = (msg :: tq.queue)}

static member Agent = MailboxProcessor.Start(fun inbox ->
                            let rec msgLoop (t : TransactionQueue) =
                                async{
                                   let! msg = inbox.Receive()
                                   let newT = TransactionQueue.UpdateState msg t
                                   printfn "%A" newT
                                   return! msgLoop newT
                                }
                            msgLoop {queue = []}
                        )

static member Add i = TransactionQueue.Agent.Post i



[<EntryPoint>]
let main argv =

// test in isolation
printfn "welcome to test"
let rec loop () =
    let str = Console.ReadLine()
    TransactionQueue.Add str
    loop ()

loop ()



0 

Результат, который я продолжаю получать, - это список только последнего ввода, состояние не сохраняется. Поэтому, если я введу «a», затем «b», затем «c», в очереди будет только значение «c» вместо «a», «b», «c».

Любая помощь или указатели будут высоко оценены!


person Community    schedule 11.05.2019    source источник
comment
Статические элементы, такие как Agent, оцениваются при каждом доступе, т. е. вы получаете новый MailboxProcessor каждый раз, когда вызываете Add.   -  person Charles Mager    schedule 12.05.2019


Ответы (1)


Так же, как свойства C# , ваш Agent на самом деле является свойством и, следовательно, ведет себя как метод с параметром void. Вот почему вы будете получать нового агента при каждом доступе к свойству Agent.

В идиоматическом F# существует два стиля реализации агентов. Если вам не нужно иметь много экземпляров агента, просто напишите модуль и инкапсулируйте внутри все, что связано с агентом. В противном случае следует использовать стиль ООП.

Код для стиля №1

module TransactionQueue =
    type private Queue = Queue of string list
    let private empty = Queue []
    let private update item (Queue items) = Queue (item :: items)
    let private agent = MailboxProcessor.Start <| fun inbox ->
        let rec msgLoop queue = async {
            let! msg = inbox.Receive ()
            return! queue |> update msg |> msgLoop
        }
        msgLoop empty
    let add item = agent.Post item

[<EntryPoint>]
let main argv =
    // test in isolation
    printfn "welcome to test"
    let rec loop () =
        let str = Console.ReadLine()
        TransactionQueue.add str
        loop ()
    loop ()

Код для стиля №2

type Queue = Queue of string list with
    static member Empty = Queue []
    static member Update item (Queue items) =
        Queue (item :: items)

type Agent () =
    let agent = MailboxProcessor.Start <| fun inbox ->
        let rec msgLoop queue = async {
            let! msg = inbox.Receive ()
            return! queue |> Queue.Update msg |> msgLoop
        }
        msgLoop Queue.Empty
    member this.Add item = agent.Post item

[<EntryPoint>]
let main argv =
    // test in isolation
    printfn "welcome to test"
    let agent = new Agent ()
    let rec loop () =
        let str = Console.ReadLine()
        agent.Add str
        loop ()
    loop ()

Обратите внимание на использование типов объединения с одним регистром для Queue тип.

person Nghia Bui    schedule 12.05.2019