Search code examples
f#mailboxprocessor

processing in iterations inside a MailboxProcessor, Seq.map vs Seq.iter


Today I had an issue implementing some simple processing inside an iteration in a MailboxProcessor.

At first I tried to do the iteration with Seq.map, but the code in the iteration was never called! Then I switched to using a Seq.iter for the iteration instead, and then the processing was done just fine...

type Agent<'Msg> = MailboxProcessor<'Msg>

...

let agent = 
    Agent.Start((fun agent -> 
                let rec loop = 
                    async { 
                        let! msg = agent.Receive()
                            match msg with
                            | SensorEvent(id, ts) -> 

                                ...

                                [for x in connections.[id] -> x]
                                |> Seq.map (fun light_id -> //Seq.iter works just fine here, Seq.map doesn't!
                                    let publish = new Publish<SimulatorBroker.SimLightOffMsg>()
                                    publish.Message <- new SimulatorBroker.SimLightOffMsg(light_id, recom_ts)
                                    peer.Publish(box publish :?> IPublish<_>)
                                )
                                |> ignore
                                return! loop
                        }
                    loop), tokenSource.Token)

What I am puzzled about is why I couldn't use Seq.map?.. and now I am wondering if it get optimized away when it isn't assigned to anything?.. or if something else weird happens when you use Seq.map inside a Mailboxprocessor..?

yes, I know Seq.iter is more appropriate for simple iterations that just return 'unit' anyways. But please forgive me, I am still learning ;).


Solution

  • Seq.map is lazy. It is not evaluated until you ask for the elements of the sequence. You can do a Seq.toList after the map and it will force it. Seq.iter is strict, it goes through all the elements of the sequence.

    Try in FSI

    Seq.initInfinite id |> Seq.map (fun x -> printfn "%A" x; x)
    

    and

    Seq.initInfinite id |> Seq.iter (fun x -> printfn "%A" x)
    

    So in your case if you want to force the execution and ignore the result, Seq.iter is more appropriate.