In general, the parameters to the pattern are the (ordered) concatenation of the parameters of the channels following [When] which is simple to specify but, as you rightly observed, easy to get wrong. So I agree with you that the syntax is a little clumsy
and could be sugared to or even replaced by something more concise and less error prone. I had actually considered precisely the explicitly typed syntax you suggest - in fact, we already had this in Comega for purely asynchronous patterns. The even better
implicitly typed variant make perfect sense, and is close to my ML heart, but is less in keeping with VB.
Apart from implementation expediency, the main reason I opted for the explicit variant is that it is similar to the [Handles] syntax, makes it easy to override patterns in subclasses and to support recursion in the body of the handler as well as debugging
and testing of patterns. Perhaps we should support the fully explicit syntax as well as sugar for the more concise, even implicitly-typed form that generates a private, non-recursive method. Heck, we could also just jettison the [Handles] analogy completely...
Another thing one might consider adding is the ability to declare that a channel handles some events and (for symmetry) that an event is raised by some pattern.
The main motivation for departing from the Polyphonic C# syntax was that decoupling channel and pattern declarations makes it easier to support inheritance and the addition of new patterns in subclasses.
We’re definitely open to suggestions and very grateful for your feedback.
You can and typically do put non-trivial code in the body of a pattern. The simple examples in the video were meant to illustrate the basic expressive power of CB patterns to express choice, join and forking operations.
For example, the more realistic one-place-buffer has non-trivial function bodies:
Public Class OnePlaceBuffer(Of T) Private Asynchronous Empty() Private Asynchronous Contains(ByVal t As T) Public Synchronous Put(ByVal t As T) Public Synchronous Take() As T Private Sub CasePutAndEmpty(ByVal t As T) When Put, Empty Contains(t) End Sub Private Function CaseTakeAndContains(ByVal t As T) As T When Take, Contains Empty() Return t End Function Public Sub New() Empty() End Sub End Class
I’m no expert on the CCR , but I believe a CCR port is equivalent to a CB asynchronous channels. As far as I know, the CCR has no direct equivalent of synchronous channels. However, you can code up a synchronous channel using a port that takes an additional
response port on which to reply to a “synchronous” sender. The sender would pass a fresh port along with the original payload then wait (block) to receive a response. Indeed, in the original join calculus that underpins CB, all channels were asynchronous and
a similar encoding was used to emulate synchronous sends. The advantage of directly supporting synchronous channels is that it avoids the additional protocol used by the encoding (which a user could get wrong, for instance, by incorrectly sharing a reply
port across two calls). The disadvantage is that it encourages the user to write blocking code with the potential for deadlock.
Unlike CB, the CCR encourages a completely asynchronous programming style but makes clever use of C#’s yield statements to let the user write asynchronous blocks of code in a sequential style, without having to explicitly CPS convert the sequential code.
I think it’s a shame VB doesn’t have iterators and hope iterators or more general coroutines are supported in future.
F#’s asynchronous workflows are similar in this regard, but use a continuation monad instead of C#’s iterators to achieve fully composable asynchronous computations.