F# MailboxProcessor async messed print statements

142 Views Asked by At

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.

2

There are 2 best solutions below

0
Jim Foye On BEST ANSWER

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, i.e. they are stepping on each other.

2
Koenig Lear On

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 e.g. () and get you the garbled result you see.

Edit: you also need to start fsi quiet so it doesn't print variable evaluations e.g. 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 e.g. have unit () output; which will in turn return only one unit () output rather than one unit () per each list item.

Related Questions in F#