Re: Proposal: split Chan and TChan into read and write ends

On Sat, Oct 27, 2012 at 8:48 PM, Shachaf Ben-Kiki
On Fri, Oct 26, 2012 at 8:23 PM, Brandon Simmons
wrote: Hi everybody, Just discovered all the great discussion here, and although my google-fu turned up no prior discussions on this subject, I apologize if it has already been discussed.
I propose that Chan and TChan should be implemented as a pair of read and write ends, initialized as follows:
newSplitChan :: IO (InChan a, OutChan a)
I've implemented this already here: http://hackage.haskell.org/package/chan-split . You can ignore the type classes I've defined; they're there for my own reasons and not part of the proposal.
Here is my best defense:
1) My own (and I assume others') use of Chans involves some bits of code which do only reads, and others which do only writes; a split implementation lets us use the type-checker to allocate read or write "permissions" when we pass around either end, and generally makes things easier to reason about. Others have independently reached this conclusion as well.
To (1), it's probably worth mentioning the idiom of pre-applying readTChan and writeTChan to a channel to produce values of types "STM a" and "a -> STM ()", which gives you something pretty similar to read-only and write-only channels. It doesn't solve every problem, but for many things it's much simpler than introducing special-purpose types that only ever have one operation applied to them (similarly you could use values "STM (STM a)" with dupTChan/newBroadcastTChan, and so on).
Shachaf
Ah! I use that myself in a library because it let me create Functor and Contravariant instances for the read and write ends respectively, and I didn't require any additional operations beyond read and write.

On 10/27/12 9:06 PM, Brandon Simmons wrote:
On Sat, Oct 27, 2012 at 8:48 PM, Shachaf Ben-Kiki
wrote: To (1), it's probably worth mentioning the idiom of pre-applying readTChan and writeTChan to a channel to produce values of types "STM a" and "a -> STM ()", which gives you something pretty similar to read-only and write-only channels. It doesn't solve every problem, but for many things it's much simpler than introducing special-purpose types that only ever have one operation applied to them (similarly you could use values "STM (STM a)" with dupTChan/newBroadcastTChan, and so on).
Ah! I use that myself in a library because it let me create Functor and Contravariant instances for the read and write ends respectively, and I didn't require any additional operations beyond read and write.
It may be worth mentioning that for this use case there's some beauty in leaving it as one type and just giving it two type parameters, a la newTChan :: STM (TChan a a) writeTChan :: a -> TChan a b -> STM () readTChan :: TChan a b -> STM b -- aka contramap @(TChan _ c) precomposeTChan :: (a -> b) -> TChan b c -> TChan a c -- aka fmap @(TChan a _) postcomposeTChan :: (b -> c) -> TChan a b -> TChan a c since this highlights the fact that the two ends together form a profunctor, i.e. a hom-functor which is covariant in its output and contravariant in its input. Of course, even with this representation it's worthwhile to be able to abstract over each of the ends in order to preclude reading or writing in various parts of your program. -- Live well, ~wren
participants (2)
-
Brandon Simmons
-
wren ng thornton