F# Expert · Lesson 1 · On the house

MailboxProcessor agents

Actor-style state with MailboxProcessor, Post, PostAndReply, and message DUs. Lesson 1 is free — on the house.

Lessons · F# Expert · Lesson 1 of 10 · On the house

MailboxProcessor agents

Actor-style state with MailboxProcessor, Post, PostAndReply, and message DUs.

Illustration for F#
Lesson 1 is on the house.

MailboxProcessor<'Msg> is an in-process agent: a single loop reads messages one at a time, so its private state needs no locks.

Model messages as a DU. Requests that need an answer carry an AsyncReplyChannel<'T>.

agent.Post msg is fire-and-forget; agent.PostAndReply(fun ch -> Get ch) blocks for the reply (PostAndAsyncReply awaits).

The loop is a recursive async function: let rec loop state = async { let! msg = inbox.Receive() ... return! loop newState }.

Unhandled exceptions kill the agent silently unless you subscribe to agent.Error.

agent

type Msg =
    | Add of int
    | Get of AsyncReplyChannel<int>

let counter = MailboxProcessor.Start(fun inbox ->
    let rec loop total = async {
        let! msg = inbox.Receive()
        match msg with
        | Add n -> return! loop (total + n)
        | Get ch ->
            ch.Reply total
            return! loop total
    }
    loop 0)

[1 .. 100] |> List.iter (fun n -> counter.Post (Add n))
printfn "%d" (counter.PostAndReply Get)

let posts = [ for _ in 1 .. 4 -> async { for _ in 1 .. 250 do counter.Post (Add 1) } ]
posts |> Async.Parallel |> Async.RunSynchronously |> ignore
printfn "%d" (counter.PostAndReply Get)
Match this when you type.

Quiz

Why doesn't agent state need locks?

Quiz

How does a caller get a reply?

Quiz

What happens to an unhandled exception in the loop by default?

Quiz

What's the final total after 1000 concurrent Add 1 posts?

Check

Match the still for: MailboxProcessor agents.

Open-book: the answers are on this page. Pass every quiz and check (5) to mark the lesson done. This visit: 0/5.