@@ -3,9 +3,9 @@ layout: documentation
33title : Documentation
44---
55
6- ### Cloud Haskell
6+ ### Cloud Haskell Platform
77
8- This is [ * Cloud Haskell* ] [ 1 ] . Cloud Haskell is a set of libraries
8+ This is the [ * Cloud Haskell Platform * ] [ 1 ] . Cloud Haskell is a set of libraries
99that bring Erlang-style concurrency and distribution to Haskell programs. This
1010project is an implementation of that distributed computing interface, where
1111processes communicate with one another through explicit message passing rather
@@ -238,14 +238,14 @@ types such as `TMVar` just as normal Haskell threads would.
238238### Typed Channels
239239
240240Channels provides an alternative to message transmission with ` send ` and ` expect ` .
241- While ` send ` and ` expect ` allow transmission of messages of any ` Serializable `
241+ While ` send ` and ` expect ` allow us to transmit messages of any ` Serializable `
242242type, channels require a uniform type. Channels work like a distributed equivalent
243243of Haskell's ` Control.Concurrent.Chan ` , however they have distinct ends: a single
244244receiving port and a corollary send port.
245245
246246Channels provide a nice alternative to * bare send and receive* , which is a bit
247- * unHaskellish * , because the processes message queue has messages of multiple
248- types, and we have to do dynamic type checking.
247+ * un-Haskell-ish * , since our process' message queue can contain messages of multiple
248+ types, forcing us to undertake dynamic type checking at runtime .
249249
250250We create channels with a call to ` newChan ` , and send/receive on them using the
251251` {send,receive}Chan ` primitives:
@@ -264,19 +264,17 @@ channelsDemo = do
264264{% endhighlight %}
265265
266266Channels are particularly useful when you are sending a message that needs a
267- response, because the code that receives the response knows exactly where it
268- came from - i.e., it knows that it came from the ` SendPort ` connected to
269- the ` ReceivePort ` on which it just received a response.
267+ response, because we know exactly where to look for the reply.
270268
271- Channels can sometimes allows message types to be simplified, as passing a
272- ` ProcessId ` to reply to isn't required. Channels are not so useful when you
273- need to spawn a process and then send a bunch a messages to it and wait for
274- replies, because we can’t send the ` ReceivePort ` .
269+ Channels can also allow message types to be simplified, as passing a
270+ ` ProcessId ` for the reply isn't required. Channels aren't so useful when we
271+ need to spawn a process and send a bunch a messages to it, then wait for
272+ replies however; we can’t send a ` ReceivePort ` since it is not ` Serializable ` .
275273
276- ReceivePorts can be merged, so you can listen on several simultaneously. In the
277- latest version of [ distributed-process] [ 2 ] , you can listen for * regular* messages
278- and on multiple channels at the same time, using ` matchChan ` in the list of
279- allowed matches passed ` receive ` .
274+ ` ReceivePort ` s can be merged, so we can listen on several simultaneously. In the
275+ latest version of [ distributed-process] [ 2 ] , we can listen for * regular* messages
276+ and multiple channels at the same time, using ` matchChan ` in the list of
277+ allowed matches passed ` receiveWait ` and ` receiveTimeout ` .
280278
281279### Linking and monitoring
282280
@@ -289,71 +287,27 @@ a set of children, starting, stopping and restarting them as necessary.
289287
290288### Stopping Processes
291289
292- Some processes, like the * outer* process in the previous example, will run until
293- they've completed and then return their value. This is just as we find with IO action,
294- and there is an instance of ` MonadIO ` for the ` Process ` monad, so you can ` liftIO ` if
295- you need to evaluate IO actions.
296-
297290Because processes are implemented with ` forkIO ` we might be tempted to stop
298291them by throwing an asynchronous exception to the process, but this is almost
299- certainly the wrong thing to do. Instead we might send a kind of poison pill,
300- which the process * ought* to handle by shutting down gracefully. Unfortunately
301- because of the asynchronous nature of sending, this is no good because ` send `
302- will not fail under any circumstances. In fact, because ` send ` doesn't block,
303- we therefore have no way to know if the recipient existed at the time we sent the
304- poison pill. Even if the recipient did exist, we still have no guarantee that
305- the message we sent actually arrived - the network connection between the nodes
306- could have broken, for example. Making this * shutdown* protocol synchronous is
307- no good either - how long would we wait for a reply? Indefinitely?
308-
309- Exit signals come in two flavours - those that can
310- be caught and those that cannot. A call to
311- ` exit :: (Serializable a) => ProcessId -> a -> Process () ` will dispatch an
312- exit signal to the specified process. These * signals* can be intercepted and
313- handled by the destination process however, so if you need to terminate the
314- process in a brutal way, you can use the ` kill :: ProcessId -> String -> Process () `
315- function, which sends an exit signal that cannot be handled.
316-
317- ------
318- #### __ An important note about exit signals__
319-
320- Exit signals in Cloud Haskell are unlike asynchronous exceptions in regular
321- haskell code. Whilst processes * can* use asynchronous exceptions - there's
322- nothing stoping this since the ` Process ` monad is an instance of ` MonadIO ` -
323- exceptions thrown are not bound by the same ordering guarantees as messages
324- delivered to a process. Link failures and exit signals * might* be implemented
325- using asynchronous exceptions - that is the case in the current
326- implementation - but these are implemented in such a fashion that if you
327- send a message and * then* an exit signal, the message is guaranteed to arrive
328- first.
329-
330- You should avoid throwing your own exceptions in code where possible. Instead,
331- you should terminate yourself, or another process, using the built-in primitives
332- ` exit ` , ` kill ` and ` die ` .
333-
334- {% highlight haskell %}
335- exit pid reason -- force ` pid ` to exit - reason can be any ` Serializable ` message
336- kill pid reason -- reason is a string - the * kill* signal cannot be caught
337- die reason -- as 'exit' but kills * us*
338- {% endhighlight %}
339-
340- The ` exit ` and ` kill ` primitives do essentially the same thing, but catching
341- the specific exception thrown by ` kill ` is impossible, making ` kill ` an
342- * untrappable exit signal* . Of course you could trap ** all** exceptions, but
343- you already know that's a very bad idea right!?
344-
345- The ` exit ` primitive is a little different. This provides support for trapping
346- exit signals in a generic way, so long as your * exit handler* is able to
347- recognise the underlying type of the 'exit reason'. This (reason for exiting)
348- is stored as a raw ` Message ` , so if your handler takes the appropriate type
349- as an input (and therefore the ` Message ` can be decoded and passed to the
350- handler) then the handler will run. This is pretty much the same approach as
351- exception handling using ` Typeable ` , except that we decide whether or not the
352- exception can be handled based on the type of ` reason ` instead of the type of
353- the exception itself.
354-
355- Calling ` die ` will immediately raise an exit signal (i.e., ` ProcessExitException ` )
356- in the calling process.
292+ certainly the wrong thing to do. Firstly, processes might reside on a remote
293+ node, in which case throwing an exception is impossible. Secondly, if we send
294+ some messages to a process' mailbox and then dispatch an exception to kill it,
295+ there is no guarantee that the subject will receive our message before being
296+ terminated by the asynchronous exception.
297+
298+ To terminate a process unconditionally, we use the ` kill ` primitive, which
299+ dispatches an asynchronous exception (killing the subject) safely, respecting
300+ remote calls to processes on disparate nodes and observing message ordering
301+ guarantees such that ` send pid "hello" >> kill pid "goodbye" ` behaves quite
302+ unsurprisingly, delivering the message before the kill signal.
303+
304+ Exit signals come in two flavours however - those that can be caught and those
305+ that cannot. Whilst a call to ` kill ` results in an _ un-trappable_ exception,
306+ a call to ` exit :: (Serializable a) => ProcessId -> a -> Process () ` will dispatch
307+ an exit signal to the specified process that can be caught. These * signals* are
308+ intercepted and handled by the destination process using ` catchExit ` , allowing
309+ the receiver to match on the ` Serializable ` datum tucked away in the * exit signal*
310+ and decide whether to oblige or not.
357311
358312----
359313
@@ -373,7 +327,7 @@ The [distributed-process-platform][18] library implements parts of the
373327in the original paper and implemented by the [ remote] [ 14 ] package. In particular,
374328we diverge from the original design and defer to many of the principles
375329defined by Erlang's [ Open Telecom Platform] [ 13 ] , taking in some well established
376- Haskell concurrency design patterns alongside .
330+ Haskell concurrency design patterns along the way .
377331
378332In fact, [ distributed-process-platform] [ 18 ] does not really consider the
379333* task layer* in great detail. We provide an API comparable to remote's
@@ -465,6 +419,12 @@ The API for `Async` is fairly rich, so reading the haddocks is suggested.
465419
466420#### Managed Processes
467421
422+ The main idea behind a ` ManagedProcess ` is to separate the functional
423+ and non-functional aspects of an actor. By functional, we mean whatever
424+ application specific task the actor performs, and by non-functional
425+ we mean the * concurrency* or, more precisely, handling of the process'
426+ mailbox and its interaction with other actors (i.e., clients).
427+
468428Looking at * typed channels* , we noted that their insistence on a specific input
469429domain was more * haskell-ish* than working with bare send and receive primitives.
470430The ` Async ` sub-package also provides a type safe interface for receiving data,
@@ -473,12 +433,12 @@ although it is limited to running a computation and waiting for its result.
473433The [ Control.Distributed.Processes.Platform.ManagedProcess] [ 21 ] API provides a
474434number of different abstractions that can be used to achieve similar benefits
475435in your code. It works by introducing a standard protocol between your process
476- and the * world around * , which governs how to handle request/reply processing,
477- exit signals, timeouts, sleep /hibernation with ` threadDelay ` and even provides
436+ and the * world outside * , which governs how to handle request/reply processing,
437+ exit signals, timeouts, sleeping /hibernation with ` threadDelay ` and even provides
478438hooks that terminating processes can use to clean up residual state.
479439
480440The [ API documentation] [ 21 ] is quite extensive, so here we will simply point
481- out the obvious differences. A implemented implemented with ` ManagedProcess `
441+ out the obvious differences. A process implemented with ` ManagedProcess `
482442can present a type safe API to its callers (and the server side code too!),
483443although that's not its primary benefit. For a very simplified example:
484444
@@ -516,6 +476,18 @@ just provides callback functions which take some state and either return a
516476new state and a reply, or just a new state. The process is * managed* in the
517477sense that its mailbox is under someone else's control.
518478
479+ A NOTE ABOUT THE CALL API AND THAT IT WILL FAIL (WITH UNHANDLED MESSAGE) IF
480+ THE CALLER IS EXPECTING A TYPE THAT DIFFERS FROM THE ONE THE SERVER PLANS
481+ TO RETURN, SINCE THE RETURN TYPE IS ENCODED IN THE CALL-MESSAGE TYPE ITSELF.
482+
483+ TODO: WRITE A TEST TO PROVE THE ABOVE
484+
485+ TODO: ADD AN API BASED ON SESSION TYPES AS A KIND OF MANAGED PROCESS.....
486+
487+ In a forthcoming tutorial, we'll look at the ` Control.Distributed.Process.Platform.Task `
488+ API, which looks a lot like ` Async ` but manages exit signals in a single thread and makes
489+ configurable task pools and task supervision strategy part of its API.
490+
519491More complex examples of the ` ManagedProcess ` API can be seen in the
520492[ Managed Processes tutorial] [ 22 ] . API documentation for HEAD is available
521493[ here] [ 21 ] .
0 commit comments