It may seem odd that two bidirectional channels are needed to implement the protocol. Couldn't we use just a single channel and change the server loop to give and take on that single channel. Note that this is allowed in Hopac and poses no problem. A job cannot send itself a message using a channel in a single synchronous operation. Explain what would go wrong if there was only one channel instead of separate getCh and putCh channels. Hint: Consider a situation with multiple clients.
The above is from the Hopac doc.
Are the new users really expected to understand the finer points of library implementation at this point in the journey? I prefer it when the documentation gives answers rather than asks questions. What is the reason why using a single channel for both take
and get
is a bad idea?
module HopacExample
open System
open Hopac
open Hopac.Infixes
type Cell<'a> = {
takeCh : Ch<'a>
putCh : Ch<'a>
}
let get c = Ch.take c.takeCh
let put c (x: 'a) = Ch.give c.putCh x
let cell x = Job.delay <| fun () ->
let c = {takeCh = Ch (); putCh = Ch ()}
let server x =
Alt.choose [
Ch.take c.putCh
Ch.give c.takeCh x ^->. x]
Job.iterateServer x server >>-. c
run <| job {
let! c = cell 1
let print () = Job.start (get c >>- fun i -> printf "%i\n" i)
let put i = Job.start (put c i)
do! print()
do! put 2
do! print()
do! put 3
do! print()
do! put 4
do! print()
do! put 5
do! print()
do! put 6
do! print()
}
Console.ReadKey()
2
3
4
4
5
6
module HopacExample2
open System
open Hopac
open Hopac.Infixes
let get c = Ch.take c
let put c (x: 'a) = Ch.give c x
let cell x = Job.delay <| fun () ->
let c = Ch ()
let server x =
Alt.choose [
Ch.take c
Ch.give c x ^->. x]
Job.iterateServer x server >>-. c
run <| job {
let! c = cell 1
let print () = Job.start (get c >>- fun i -> printf "%i\n" i)
let put i = Job.start (put c i)
do! print()
do! put 2
do! print()
do! put 3
do! print()
do! put 4
do! print()
do! put 5
do! print()
do! put 6
do! print()
}
Console.ReadKey()
5
5
5
5
5
6
There are some differences, but nothing I would not attribute to vagaries of concurrency.
The reason why you need two channels is to force the users to go through the server. You'd be able to tell what the problem with using just a single channel is if you removed the server entirely. With just a single channel, the clients might bypass the server and communicate to each other directly.