简体   繁体   中英

F# MailboxProcessor async messed print statements

I am trying to make a bar simulator that is processing orders. The message that is sent to the agent is of this type.

type DTGCafeMessage =
    | OrderDrink of Drink * float
    | LeaveAComment of string

The agent is a bar class implemented below.

type Bar() =
    let dtgCafeAgent =
        MailboxProcessor.Start
            (fun inbox ->
                let rec messageLoop () =
                    async {
                        let! msg = inbox.Receive()

                        match msg with
                        | OrderDrink (drink, amount) ->
                            let drinkPrice : float = getPrice drink
                            let mutable totalPrice = ((drinkPrice: float) * (amount: float))

                            if drink.type' = Coffee then
                                totalPrice <- dgtVAT totalPrice VAT

                            printfn
                                "Please pay DKK%d for your %d %A %A drinks. %s!"
                                (Convert.ToInt32(totalPrice))
                                (Convert.ToInt32(amount))
                                (string drink.type')
                                (string drink.size)
                                "Thanks!"
                        | LeaveAComment (comment) -> printf "Comment: %A" comment

                        return! messageLoop ()
                    }

                messageLoop ())

    member this.Order msg = dtgCafeAgent.Post msg


When I send the messages to the agent, it is printing stuff in a very messed way.

let bar = Bar()
let testDrink = { type' = Coffee; size = Small }

bar.Order(OrderDrink({ type' = Coffee; size = Small }, 2.0))

let orderDrinks =
    [ (OrderDrink({ type' = Coffee; size = Small }, 1.0))
      (OrderDrink({ type' = Coffee; size = Medium }, 2.0))
      (OrderDrink({ type' = Juice; size = Small }, 3.0))
      (OrderDrink({ type' = Soda; size = Medium }, 4.0))
      (OrderDrink({ type' = Milk; size = Large }, 5.0)) ]

orderDrinks |> List.map (fun o -> bar.Order o)

// output
> orderDrinks |> List.map (fun o -> bar.Order o);;
valPlease pay DKK24 for your 1  "Coffee" "Small" drinks. Thanks!!
it Please pay DKK72 for your 2 "Coffee" :"Medium" drinks. Thanks!!
 unitPlease pay DKK 45 for your 3 list"Juice" "Small" drinks. Thanks!!
 =Please pay DKK51 for your 4  "Soda" "Medium" drinks. Thanks!!
[()Please pay DKK;125 for your 5 "Milk"  "Large" drinks. Thanks!!
(); (); (); ()]

As I see, it should print every statement as written in the code.

Fsi is evaluating your expression. Since bar.Order() returns unit, the result of your expression is [(); (); (); (); ()]. So Fsi wants to print something like

val it : unit list = [(); (); (); (); ()]

Meanwhile, the mailbox processor is on another thread working through its queue, printing messages as it does so. The two threads are both sending text to the same output, ie they are stepping on each other.

Just do

orderDrinks |> List.iter bar.Order

if you just want to print the results.

List.map would instead convert the output, but the output is of type unit eg () and get you the garbled result you see.

Edit: you also need to start fsi quiet so it doesn't print variable evaluations eg fsi --quiet

Otherwise the behaviour is correct since it shows how the mailbox processor starts executing tasks, and hands over control to the caller before it finishes. Thus the caller fsi, starts printing val it: unit (). At the same time that the mailbox processor is working. Both processes writing to the console at the same time, producing garbled output.

Still List.Iter is used when you are executing functions that have no output eg have unit () output; which will in turn return only one unit () output rather than one unit () per each list item.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM