I am struggling at the check and validate function for Lwt_pool.create and have some questions here.
val create :
int ->
?check:('a -> (bool -> unit) -> unit) ->
?validate:('a -> bool Lwt.t) -> (unit -> 'a Lwt.t) -> 'a t
First of all, let me describe the background of my usage.
I wish to use Lwt_pool to manage a pool of database connections. The database is MongoDB and the driver was made by myself (Mongo.ml). The driver is actually simple that it is just a TCP (Unix.file_descr) connection to the MongoDB server and send requests / receive responses with the server.
`create n ?check ?validate f` creates a new pool with at most n members. f is the function to use to create a new pool member.
An element of the pool is validated by the optional validate function before its Lwt_pool.use. Invalid elements are re-created.
The optional function check is called after a use of an element failed. It must call its argument excatly one with true if the pool member is still valid and false otherwise.
above is the documentation for create
So here are my questions:
From the doc, I understand validate is to validate the connection before using it.
so my first question is How can I check the availability of a Unix.file_descr? I only know that in order to check it, I have send something through it, right? But if I send something through my connection in order to check, then I guess it would be urgly and I anyway want to send something out via Lwt_pool.use, why bother do similar things before use?
My second question is about check.
So check will be used after use. From the doc, I really can't understand. check is a function which take a my_db_connection (in my case) and a (fun b -> unit) as parameter. Who will provide (fun b -> unit)? Does Lwt_pool itself has such a function? or I should provide it? To do what then?
thanks
I don't know anything about Lwt, but one thing to do is to wait to use a validate function until you see why you would need it. That's why it's an optional parameter (I suspect).
One thing you can do with a Unix file descriptor is to figure out whether it's attached to a network socket:
let is_a_socket fd = (Unix.fstat fd).st_kind = S_SOCK
Maybe this will be useful, though someone with Lwt experience can probably give a better answer.
Related
I have a function
convertMsg : Msg1 -> List Msg2
where Msg1 and Msg2 are certain message types. And I would like to turn this into a function:
convertCmd : Cmd Msg1 -> Cmd Msg2
Which would for every message in the batch replace it with some messages possibly none or more than 1.
As a Haskell programmer at heart I immediately reach for a monadic bind ((>>=) in Haskell and andThen in the Elm parlance), a function with the type:
bind : (a -> Cmd b) -> Cmd a -> Cmd b
I can easily change my convertMsg to be the following:
convertMsg : msg1 -> Cmd Msg2
At which point it would be just perfect for the bind.
But looking in Platform.Cmd, there isn't such a function I can find. There's a map which is similar, but convertMsg can't really be convertMsg : Msg1 -> Msg2 since it doesn't always give back exactly one message.
Is there a way to achieve this? Is there some limitation to the Cmd type that would prevent this sort of thing?
What you're trying to do to messages, how you might, and whether it's a good plan
I promise I'll try to answer what I think you're trying to do, but first I think there's a more important thing...
You're perhaps assuming that Cmd is analogous to IO from Haskell, but Cmd is asynchronous and isn't designed to chain actions. Your update is what glues consequences to outputs:
update : Msg -> Model -> (Model,Cmd Msg)
At the end of your update, you can issue a Cmd Msg to ask elm to do something externally, usually passing it a constructor with which it can wrap its output. This output comes back to your update function.
What you don't do is chain Cmds together as you would in a monad. There's no bind for Cmd, or to put it another way, the only bind for Cmd is your update function!
Now I suppose that if you wanted to catch a MyComplexMsg : Msg and turn it into [SimpleMsg1,SimpleMsg2], you could pattern match for it in your update function, leave the model unchanged and issue a new Cmd Msg, but what command would you be running the second time?
You could certainly take a pure Msg -> List Msg function and use Cmd.map to apply it, or apply it manually in the pattern match at the beginning of update, like
update msg model = case msg of
MyComplexMsg -> myHandler [SimpleMsg1,SimpleMsg2]
...
or even go full state monad style with
update msg model0 = case msg of
MyComplexMsg ->
let
(model1,cmd1) = update SimpleMsg1 model0
(model2,cmd2) = update SimpleMsg2 model1
in
(model2,Cmd.batch [cmd1,cmd2])
to try to emulate monadic bind, but I don't know why you might ever want this, and a lot of advice in the elm literature and community is that if you're calling update from update you're probably doing it wrong. Make a separate single-purpose helper function for that stuff instead of re-running your entire program logic twice!
Let go of your need to have a monad
I suspect that what's going wrong is that you're not letting go of a monadic control flow mentality. update is where it's at. update is where you make things happen. User input and asynchronous messages are your drivers, not sequencing. Cmd is just for communicating externally. You don't plumb the results back in, the elm architecture does that for you. Just handle the result of your Cmd (which will arrive as a message) as a branch in your update and it'll all progress nicely, and if the user presses some button of their own choice without you making it happen, so be it. You can handle that too.
I worry that you're trying to write a monad transformer stack in elm, which is a bit like trying to write an object oriented programming library in haskell. Haskell doesn't do object oriented programming, and the sooner folks drop the OO thinking and let go of their need to bundle data and functions together, the sooner they're writing good haskell code. Elm doesn't do typeclasses, it does model/view/update, and does it extraordinarily well. Let go of your need to find and use a monad to control the flow of your program, and instead respond to what messages you're given. Make a Msg for something you want to happen, provide a way to trigger it appropriately in your view and then handle it in your update.
When should one message become three messages?
If one of your messages is really three messages, why isn't it three messages already? If it's just that in response to that particular message, you just need to do three things to your model and issue five commands, why not just have one message and get update to do those three things to your model using pure code and issue the five commands in a batch?
If you need to log the successful login, then get the user's photo, then query the database for their recent activity, then display it all, then I disagree about the immediateness, and they're all asynchronous. You can issue commands to do each of those things in a batch, and when the responses come back you will need to separately deal with each - update your model with the image when it arrives, with the list of recent activity when it arrives. Once your model is in the state that the picture and the recent activity are both there you can change the view, but why not show each as soon as they're there?
Using monads sometimes trains us to think sequentially when we're doing effects programming when we needn't, but now, finally, I'll address what to do when there is a compelling need to sequence commands.
Genuinely necessary sequential commands
Perhaps there really is something sequential that you need. Maybe you have to query some data store for something before you send some request elsewhere. You still don't use a bind, you just use your update:
update msg model = case msg of
StartsMultiStageProcess userID ->
({model|multiStageProcessStatus = RequestedData}
, getDataPart1 userID PartOneReceived )
PartOneReceived userData ->
({model|multiStageProcessStatus = Fired}
, fireRockets userData.nemesis.location RocketResult)
RocketResult r -> if model.multiStageProcessStatus == Fire then
case r of
Ok UtterlyDestroyed ->
....
Ok DamagedBeyondUse ->
....
Err disappointment ->
....
...
A handy point is that if your model doesn't have the required data, it automatically won't show the missing data in the view (sum types for the win), and you can store whatever state your multistage process is in in the model.
You might prefer to put all of those messages into a new type so it becomes indented once further in the handler and won't ever be mixed with other ones in the order, like
MSPmsg msg -> case msg of
Started userId ->
...
GotPartOne userData ->
but much more likely use a helper function like MSPmsg msg -> updateMultiStageProcess.
Concluding advice
Maybe there's some great use case for delving into messages and commands and editing them that you haven't made explicit, but Cmd is opaque and all you can do is issue them and handle the resulting messages, so I'm sceptical but definitely interested.
Also in giving you update to write, it's almost like they've given you the app-specific bind to write (but it's not a functor and you absolutely do look at the data), so they've given you the keys to the Tesla. It takes a bit of getting used to but you're really going to like what happens at the traffic lights. Don't attempt to dismantle the door hinges until you've learned to drive it.
Edit: Your specific use case: inter-page communication
It turns out in chat that you're trying to get messages from one page to be usable in other pages or the overall update - sometimes one page needs to tell the app to change page and tell the new page to start an animation. I might have skipped all the advice above if I'd known that initially, but I think it's good advice for anyone coming from Haskell and I'm leaving it in!
Multiple messages
I still think it's important to accept that sometimes a single message needs to cover multiple actions, and you sort that out in your update function rather than try to create multiple messages in response to a single user action.
Lots of elm folks give the advice that your messages, rather than describing something to do like AddProduct they should describe something that happened in the past, partly because that's how messages come to you in your update so your mental model of what the elm runtime is doing is accurate, and partly because you're less likely to want to make two messages and do weird message translations when you ought to make one message.
Do multiple things in your ClickedViewOffers branch of update rather than try to make both a SwitchToOffersPage and a
AnimatePickOfTheDay message.
I'd like to point out that your idea to convert your messages and filter them somehow within the messages type is doing it in the wrong place. Filtering so that your Home page doesn't get all the messages for your Login page is something you have to do in update anyway - don't try to filter them while you're making or passing the messages. update is where it's at for deciding what to do in response to user input. Messages are for describing user input.
OK, but how do you get messages to cross the barriers between pages?!
There are a few ways to achieve this and it might be worth looking into different ways of making a Single Page Application (SPA) in Elm. I found this article by Rogério Chaves on Medium quite enlightening on the topic of various ways of organising messages from child page to parent app. He's done the TodoMVC app all the different ways in this repo A stack overflow post is better if it inlines ideas, so here we go:
Common Msg type across all pages
This can work by having a separate module for your message types which all your modules import. Messages look like ProductsMsg (UserCreatedNewProduct productRecord), as they might well do anyway, but because all the message types are global you can call another page's methods.
Individual pages also return an OutMsg from their update function
Use better names than these (eg Login.Msg rather than LoginMsg), but...
loginPageUpdate : LoginMsg -> LoginModel -> (LoginModel,Cmd LoginMsg,OutMsgFromLogin)
update : GlobalMsg -> GlobalModel -> (GlobalModel,Cmd GlobalMsg)
update msg model = case msg of
LoginMsg loginMsg ->
let (newLoginModel,cmd,outMsgFromLogin) = loginPageUpdate loginMsg model.loginModel
in
...
(You'd need NoOp :: OutMsgFromLogin or use Maybe OutMsgFromLogin there. I'm not a fan of NoOp. It's terribly tempting to use it for unimplemented features, and it's the king of all divorced-from-user-intentions messages that doesn't explain why you ought to do nothing or how you came to write something where you generated a purposeless message. I think it's a code smell that there's a better way of writing something.)
Have a record of messages that you later use to translate your page's Msgs messages into global messages.
(Again, use better domain-specific names, I'm trying to convey usage in my names.)
type LoginMessagesRecord globalMsg =
{ internalLoginMsgTag : LoginMsg -> globalMsg
, loginSucceeded : User -> globalMsg
, loginFailed : globalMsg
, newUserSuccessfullyRegistered : User -> globalMsg
}
and in your main, you would specify these:
loginMessages : LoginMessagesRecord GlobalMsg
loginMessages =
{ internalLoginMsgTag = LocalLoginMsg
, loginSucceeded = LoginSucceeded
, loginFailed = LoginFailed
, newUserSuccessfullyRegistered = NewUserSuccessfullyRegistered
}
You can either parameterise functions in your Login code with those so they all consume a LoginMessagesRecord and produce a msg, or you can use a genuinely local message type and write a translation helper in your Login module:
type HereOrThere here there = Here here | There there
type LocalLoginMessage = EditedUserName String | EditedPassword String | ....
type MessageForElsewhere = LoggedIn User | DidNotLogIn | MadeNewAccount User
type alias LoginMsg = HereOrThere LocalLoginMessage MessageForElsewhere
loginMsgTranslator : LoginMessagesRecord msg -> LoginMsg -> msg
loginMsgTranslator
{ internalLoginMsgTag
, loginSucceeded
, loginFailed
, newUserSuccessfullyRegistered
}
loginMsg = case loginMsg of
Here msg -> internalLoginMsgTag msg
There msg -> case msg of
LoggedIn user -> loginSucceeded user
DidNotLogIn -> loginFailed
MadeNewAccount user -> newUserSuccessfullyRegistered user
and then you can use Html.map loginMsgTranslator loginView in your global view, or Element.map loginMsgTranslator loginView if you're using the utterly brilliant html&css-free way to write elm apps, elm-ui.
Summary / takeaway
Have a single message describing a user intention and use update to handle all the consequences.
Don't edit the messages, respond appropriately in the update
The user is in control. The runtime is in control. You're not in control. Don't generate messages yourself, just respond to them. If you're generating messages rather than the user or the runtime, you're using elm in a weird way that'll be hard.
Your program logic largely resides in update. It doesn't reside in message. Don't try to make things happen in message, just describe what the user did or what the system did in the message.
Use case statements and descriptive tags in message types to help choose which update helper function to run. It can often help to use union types to describe how local a message is. Sometimes you use a local updating function, sometimes a global one.
You might want to also read this reddit thread about scaling elm apps that Rogério Chaves references.
I am trying to test a MailboxProcessor in F#. I want to test that the function f I am giving is actually executed when posting a message.
The original code is using Xunit, but I made an fsx of it that I can execute using fsharpi.
So far I am doing this :
open System
open FSharp
open System.Threading
open System.Threading.Tasks
module MyModule =
type Agent<'a> = MailboxProcessor<'a>
let waitingFor timeOut (v:'a)=
let cts = new CancellationTokenSource(timeOut|> int)
let tcs = new TaskCompletionSource<'a>()
cts.Token.Register(fun (_) -> tcs.SetCanceled()) |> ignore
tcs ,Async.AwaitTask tcs.Task
type MyProcessor<'a>(f:'a->unit) =
let agent = Agent<'a>.Start(fun inbox ->
let rec loop() = async {
let! msg = inbox.Receive()
// some more complex should be used here
f msg
return! loop()
}
loop()
)
member this.Post(msg:'a) =
agent.Post msg
open MyModule
let myTest =
async {
let (tcs,waitingFor) = waitingFor 5000 0
let doThatWhenMessagepostedWithinAgent msg =
tcs.SetResult(msg)
let p = new MyProcessor<int>(doThatWhenMessagepostedWithinAgent)
p.Post 3
let! result = waitingFor
return result
}
myTest
|> Async.RunSynchronously
|> System.Console.WriteLine
//display 3 as expected
This code works, but it does not look fine to me.
1) is the usage of TaskCompletionSource normal in f# or is there some dedicated stuff to allow me waiting for a completion?
2) I am using a second argument in the waitingFor function in order to contraint it, I know I could use a type MyType<'a>() to do it, is there another option? I would rather not use a new MyType that I find cumbersome.
3) Is there any other option to test my agent than doing this? the only post I found so far about the subject is this blogpost from 2009 http://www.markhneedham.com/blog/2009/05/30/f-testing-asynchronous-calls-to-mailboxprocessor/
This is a tough one, I've been trying to tackle this for some time as well. This is what I found so far, it's too long for a comment but I'd hesitate to call it a full answer either...
From simplest to most complex, depends really how thoroughly you want to test, and how complex is the agent logic.
Your solution may be fine
What you have is fine for small agents whose only role is to serialize access to an async resource, with little or no internal state handling. If you provide the f as you do in your example, you can be pretty sure it will be called in a relatively short timeout of few hundred milliseconds. Sure, it seems clunky and it's double the size of code for all the wrappers and helpers, but those can be reused it you test more agents and/or more scenarios, so the cost gets amortized fairly quickly.
The problem I see with this is that it's not very useful if you also want to verify more than than the function was called - for example the internal agent state after calling it.
One note that's applicable to other parts of the response as well: I usually start agents with a cancellation token, it makes both production and testing life cycle easier.
Use Agent reply channels
Add AsyncReplyChannel<'reply> to the message type and post messages using PostAndAsyncReply instead of Post method on the Agent. It will change your agent to something like this:
type MyMessage<'a, 'b> = 'a * AsyncReplyChannel<'b>
type MyProcessor<'a, 'b>(f:'a->'b) =
// Using the MyMessage type here to simplify the signature
let agent = Agent<MyMessage<'a, 'b>>.Start(fun inbox ->
let rec loop() = async {
let! msg, replyChannel = inbox.Receive()
let! result = f msg
// Sending the result back to the original poster
replyChannel.Reply result
return! loop()
}
loop()
)
// Notice the type change, may be handled differently, depends on you
member this.Post(msg:'a): Async<'b> =
agent.PostAndAsyncReply(fun channel -> msg, channel)
This may seem like an artificial requirement for the agent "interface", but it's handy to simulate a method call and it's trivial to test - await the PostAndAsyncReply (with a timeout) and you can get rid of most of the test helper code.
Since you have a separate call to the provided function and replyChannel.Reply, the response can also reflect the agent state, not just the function result.
Black-box model-based testing
This is what I'll talk about in most detail as I think it's most general.
In case the agent encapsulates more complex behavior, I found it handy to skip testing individual messages and use model-based tests to verify whole sequences of operations against a model of expected external behavior. I'm using FsCheck.Experimental API for this:
In your case this would be doable, but wouldn't make much sense since there is no internal state to model. To give you an example what it looks like in my particular case, consider an agent which maintains client WebSocket connections for pushing messages to the clients. I can't share the whole code, but the interface looks like this
/// For simplicity, this adapts to the socket.Send method and makes it easy to mock
type MessageConsumer = ArraySegment<byte> -> Async<bool>
type Message =
/// Send payload to client and expect a result of the operation
| Send of ClientInfo * ArraySegment<byte> * AsyncReplyChannel<Result>
/// Client connects, remember it for future Send operations
| Subscribe of ClientInfo * MessageConsumer
/// Client disconnects
| Unsubscribe of ClientInfo
Internally the agent maintains a Map<ClientInfo, MessageConsumer>.
Now for testing this, I can model the external behavior in terms of informal specification like: "sending to a subscribed client may succeed or fail depending on the result of calling the MessageConsumer function" and "sending to an unsubscribed client shouldn't invoke any MessageConsumer". So I can define types for example like these to model the agent.
type ConsumerType =
| SucceedingConsumer
| FailingConsumer
| ExceptionThrowingConsumer
type SubscriptionState =
| Subscribed of ConsumerType
| Unsubscribed
type AgentModel = Map<ClientInfo, SubscriptionState>
And then use FsCheck.Experimental to define the operations of adding and removing clients with differently successful consumers and trying to send data to them. FsCheck then generates random sequences of operations and verifies the agent implementation against the model between each steps.
This does require some additional "test only" code and has a significant mental overhead at the beginning, but lets you test relatively complex stateful logic. What I particularly like about this is that it helps me test the whole contract, not just individual functions/methods/messages, the same way that property-based/generative testing helps test with more than just a single value.
Use Actors
I haven't gone that far yet, but what I've also heard as an alternative is using for example Akka.NET for full-fledged actor model support, and use its testing facilities which let you run agents in special test contexts, verify expected messages and so on. As I said, I don't have first-hand experience, but seems like a viable option for more complex stateful logic (even on a single machine, not in a distributed multi-node actor system).
The Ocsigen/Eliom tutorial starts with an example of an application that serves up "Hello, world!" as HTML:
open Eliom_content.Html5.D
let main_service =
Eliom_registration.Html5.register_service
~path:["graff"]
~get_params:Eliom_parameter.unit
(fun () () ->
Lwt.return
(html
(head (title (pcdata "Page title")) [])
(body [h1 [pcdata "Graffiti"]])))
How would one serve this as JSON instead? Specifically, how does one register a JSON service, and what library/combinators should be used to generate/serialize the JSON (js_of_ocaml?)?
If you want to communicate with a client side Eliom program, you do not need to serialize yourself your data to JSON. Serialization/Deserialization of any OCaml type is done automatically by Eliom. Just use OCaml services (or, simpler: server functions and call the function from your OCaml client side program).
If you want to use your own JSON format, you need to have your own serialisation function to JSON (or for example to use some ocaml library like json-wheel to generate JSON). In that case, you can register your service using Eliom_registration.String instead of Eliom_registration.Html5. The handler function must return the JSON value as a string, and a the content-type.
It is even possible to define yourself your own registration module, to be used instead of Eliom_registration.String. Thus, you can use an OCaml representation of the JSON value (and you don't call the serializer yourself). Have a look on how modules like Eliom_registration.String are implemented to know how to do that.
I'm not sure to understand what you want to do, but, about JSON, you can use "deriving" (cf. Deriving_Json) to create a JSON type by using an OCaml type like this:
type deriving_t = (string * string) deriving (Json)
This will create the JSON type corresponding to the OCaml type.
Here the way of using this type to communicate with the server (if you don't know server functions, here the documentation about it and about client values on server side):
(* first you have to create a server function (this allowed the client to call a function from the server *)
let json_call =
server_function
Json.t<deriving_t>
(fun (a,b) ->
Lwt.return (print_endline ("log on the server: "^a^b)))
(* let say that distillery has already generate all the needed stuff (main_service, Foobar_app, etc..) *)
let () =
Foobar_app.register
~service:main_service
(fun () () ->
{unit{
(* here I call my server function by using ocaml types directly, it will be automatically serialize *)
ignore (%json_call ("hello", "world"))
}};
Lwt.return
(Eliom_tools.F.html
~title:"foobar"
~css:[["css";"foobar.css"]]
Html5.F.(body [
h2 [pcdata "Welcome from Eliom's distillery!"];
])))
If you want to use some client/server communication, you should take a look to Eliom_bus, Eliom_comet or Eliom_react.
(sorry, I can't make more than 2 links :) but you will find the documentation on the ocsigen.org website).
Hope that can help you.
I'm writing a little clojure pub/sub interface. It's very barebones, only two methods that will actually be used: do-pub and sub-listen. sub-listen takes a string (a sub name) and do-pub takes two strings (a sub name and a value).
I'm still fairly new at clojure and am having some trouble coming up with a workable way to do this. My first thought (and indeed my first implementation) uses a single agent which holds a hash:
{ subname (promise1 promise2 etc) }
When a thread wants to sub it conj's a promise object to the list associated with the sub it wants, then immediately tries to de-reference that promise (therefore blocking).
When a pub happens it goes through every item in the list for the sub and delivers the value to that item (the promise). It then dissoc's that subname from the map and returns it to the agent.
In this way I got a simple pub sub implementation working. However, the problem comes when someone subs, doesn't receive a pub for a certain amount of time, then gets killed due to timeout. In this scenario there will be a worthless promise in the agent that doesn't need to be, and moreover this will be a source of a memory leak if that sub never gets pub'd.
Does anyone have any thoughts on how to solve this? Or if there is a better way to do what I'm trying to do overall (I'm trying to avoid using any external pre-cooked pubsub libraries, this is a pet project not a work one)?
You can do something like this:
Create an atom
publish function will update the atom value by the passed in value to the function
Subscribers can use add-watch on the atom to be notified of when the atom value changes i.e due to call to publish function
Use remove-watch to remove the subscription.
This way you will have a very basic pub-sub system.
I have marked Ankur's answer as the solution but I wanted to expand on it a bit. What I ended up doing is having a central atom that all client threads do an add-watch on. When a pub is done the atom's value is changed to a vector containing the name of the sub and the value being pub'd.
The function the clients pass to add-watch is a partial function which looks like
(partial (fn [prom sub key ref _old new] ...) sub prom)
where prom is a promise previously generated. The client then blocks while waiting on that promise. The partial function checks if the sub in new is the same as sub, if so it removes the watch and delivers on the promise with the value from new.
I have been slowly examining all of the features that F# brings to the table. One that has particularly piqued my interest is the MailboxProcessor.
The equivalent of this in C# would most likely use locks. Can we consider the MailboxProcessor as a replacement for locks?
In the following example, am I doing
anything particularly naive or can
you see anything that might be
improved?
module Tcp =
open System
open System.Collections.Generic
open System.Net
open System.Net.Sockets
open System.Threading
type SocketAsyncMessage =
| Get of AsyncReplyChannel<SocketAsyncEventArgs>
| Put of SocketAsyncEventArgs
| Dispose of AsyncReplyChannel<MailboxProcessor<SocketAsyncMessage>>
type SocketAsyncEventArgsPool(size:int) =
let agent =
lazy(MailboxProcessor.Start(
(fun inbox ->
let references = lazy(new List<SocketAsyncEventArgs>(size))
let idleReferences = lazy(new Queue<SocketAsyncEventArgs>(size))
let rec loop () =
async {
let! message = inbox.Receive()
match message with
| Get channel ->
if idleReferences.Value.Count > 0 then
channel.Reply(idleReferences.Value.Dequeue())
else
let args = new SocketAsyncEventArgs()
references.Value.Add args
channel.Reply args
return! loop()
| Put args ->
if args = null then
nullArg "args"
elif references.Value.Count < size then
idleReferences.Value.Enqueue args
else
if not(references.Value.Remove args) then
invalidOp "Reference not found."
args.Dispose()
return! loop()
| Dispose channel ->
if references.IsValueCreated then
references.Value
|> Seq.iter(fun args -> args.Dispose())
channel.Reply inbox
}
loop())))
/// Returns a SocketAsyncEventArgs instance from the pool.
member this.Get () =
agent.Value.PostAndReply(fun channel -> Get channel)
/// Returns the SocketAsyncEventArgs instance to the pool.
member this.Put args =
agent.Value.Post(Put args)
/// Releases all resources used by the SocketAsyncEventArgsPool.
member this.Dispose () =
(this:>IDisposable).Dispose()
interface IDisposable with
member this.Dispose() =
if agent.IsValueCreated then
(agent.Value.PostAndReply(fun channel -> Dispose channel):>IDisposable).Dispose()
Mailboxes (and similar constructs) are used in programming models that don't use locks, as they're inherently built around asynchronous processing. (Lack of shared mutable state is another requirement of this model).
The Actor model can be thought of as a series of single-threaded mini-applications that communicate by sending and receiving data from each other. Each mini-application will only be run by a single thread at a time. This, combined with the lack of shared state, renders locks unnecessary.
Procedural models (and most OO code is, at its heart, procedural), use thread-level concurrency, and synchronous calls to other objects. The Actor model flips this around - calls (messages) between objects are asynchronous, but each object is completely synchronous.
I don't know enough F# to really analyze your code, frankly. It does look like you're trying to stick a synchronous-looking shell around your mailbox, and I wonder if that's really the best thing to do (vs. embracing the mailbox model fully). In your implementation, it does appear that you're using it as a replacement for a lock.
To first part of your question:
The MailboxProcessor class is a message queue running on its own thread. You may send an message
to the MailboxProcessor from any thread as asynchronously as synchronously.
Such model allows to communicate between threads through message passing instead of using locks/mutexes/ipc mechanics.