@felix9: Slightly better for sure, because there's something you can grab. Passing it around though is still cumbersome: you'd need to pass the static event object, as well as the instance to which it applies. All the remainder points made in my .NET events comparison slide hold here too: their "events as data source" characteristic is not apparent, composition lacks, etc. What you got though is a slightly nicer FromEvent bridge.
In fact, WPF events are not strictly more first class than .NET events, because you still have to pass two pieces of information around: the event object and the instance to which it applies. That's different with delegates, which can capture both the target method and the instance to go with it (remember all the function pointer "fun" in native C/C++ land to achieve a similar effect?).
Having said all of this, again: we're not replacing existing event sources or sources of asynchrony. Instead, we can build bridges and expose those as observable sequences. Building this particular bridge is incredibly straightforward, too.
PS: In a former life, I was a developer on WPF, so yes, I'm very well-aware of what that particular platform got too .
@Waldemar: Thanks for the feedback. In fact I have two of those clickers (<AdWarning>Microsoft Wireless Notebook Presenter Mouse 8000</AdWarning>), which I either forget to bring or forget to switch off such that the battery dies prematurely.
So yes, this was a little bit of jumping around on stage, which is good for my "calories burned count" anyway. Luckily some conference venues provide this kind of equipment .
@AdamSpeight2008: I've actually mentioned during my talk (as far as I remember) that we have an overload that allows you to do more or less what you're describing, by passing in add and remove handler lambda expressions (which will be using the .NET event itself using +- and -= operations, respectively).
Since I like to do all demos live and always avoid to import code snippets on stage (sure, I could build the entire Windows kernel live on stage by dragging and dropping a code snipet of a few million lines of code, that would be immediately obvious to the audience ), I went for the shorter one. Following this approach of live coding allows the brainwaves of the audience to be in sync with mine while producing the code.
That said, this overload is there as the closest one to being able to pass in an event. However, .NET events are merely metadata citizens and are not first-class constructs: in other words, you can't grab them nor pass them around (as opposed to methods which you can grab by means of delegate types).
The sample of the LINQ to WQL provider can (finally) be downloaded. More information can be found on
this post on the Rx forum. Please use the forum for further questions and/or discussions on the subject.
There are a few samples where the form quits before event subscriptions are disposed; it may be that's the case you're hitting, which looks similar in terms of exceptions. I'll have a look. Thanks for letting me know.
One versus many is a popular debate amongst language people. In fact, certain functional languages that will remain unnamed encode a lot of stuff using lists (e.g. "nullability" in terms of zero-length lists to represent "not present"), while others introduce
whole separate language features to deal with 0 versus 1 cardinality (e.g. "option"). I'm much more in the former camp as
many can encode one, but not vice-versa.
Whether or not the IE/IQ design for aggregates it the right one is something that can be debated for a long time in a similar manner. If we're doing LINQ to SQL using it anyway, isn't a single result a single-column, single-row table? People have always
used different methods on low-level APIs a la SqlCommand depending on the desired outcome. While you could use ExecuteReader to get a scalar value back, ExecuteScalar makes things simpler. But if LINQ generalizes operators over sequences, something can be
said about it being closer to the ExecuteReader approach. With the sole argument of one more method call adding friction, xs.Sum().Single() isn't too bad. This said, I do buy the arguments made.
The thing that's certain is that in an asynchronous world you don't want to leave the monad. There are multiple reasons for that. One is
cancellation, but the more important one is to preservecomposition. The former is definitely an IO thing, but the latter applies equally well in the world of IE. We may introduce similar aggregates in our Interactive Extensions (Ix) assembly for LINQ to Objects as well. Simply because the original
LINQ design didn't account for further composition, doesn't mean it's not better. In particular, with operators like Amb and Timeout (amongst many others) introduced in Rx and Ix, composition for aggregates is incredibly useful. Also, a different return type
outside IE requires people to write different code to do data binding to the result of an aggregation versus a grid-shaped result.
Finally, in the IQ case, the fact you loose the expression tree breaks composition on that level in certain cases. Not if you're "lucky" and things like Sum() appear in a selector lambda expression. But if you want to translate computations involving multiple
aggregates (agreed, not often done in today's world), you're left out in the dark as there's no way to get the tree representing Sum without triggering its execution (unless you manually reconstruct it using infoof(Queryable.Sum) in a MethodCallExpression).
Btw, if aggregates stayed in IE, computation over multiple aggregates could be done easily using operators like Zip, which is just a "lifted binary operator" for single-element sequences.
Bridging Rx with MSMQ is totally doable. At first sight, it doesn't look like there's a domain-specific queue query language you can apply to contents of a query, so the queryable approach seems redundant. Nonetheless, some of the System.Messaging APIs may
benefit from mapping onto Rx equivalent operators. For example, the TimeSpan parameter used to specify a timeout on various Send/Receive operations is an ideal candidate for exposure through the Timeout operator of Rx.
Concerning this bridging, simply creating operators that expose a message queue as an IObservable and vice versa would likely suffice. In fact, I've done such a thing in the past. Transactions could be totally part of this too. Basically, you'd check whether
a MessageQueue object is marked as Transactional, and if so, you'd likely do something along those lines:
When importing an IObservable to MSMQ, you'd likely want a transactional send of many OnNext messages, rolling back the transaction on OnError and committing it on OnCompleted. This won't work well for infinite sequences of course (and likely you don't
want to do a transactional "send just one message" kind of thing).
When importing an MSMQ queue as an IObservable, you'd likely want transactional receive to work on a per-message basis, only committing the transaction if the OnNext message you send out to the consumers gracefully returns. That is, you guarantee the user
has seen the message successfully and could handle it.
If this automatic transaction management doesn't work for you, a better option may be to have the user control the transaction himself, simply by use of MessageQueueTransaction objects. Not as sexy or smart as the above, but maybe necessary to accommodate
for various more complicated scenarios.
I'll leave it as an exercise to the reader to implement such bridging operators.
(Edit: wanted to paste code, but it went terribly wrong; will try again later)
Oh, and the "visually close" argument raised earlier is rather weak, especially since homo-iconicity is all about that . Is x => x too much like ... x => x, while they may be radically different behind the scenes (delegate versus expression tree)? Also
keep in mind the interface is not what users of providers see, at all.
Expression Trees on MSDN, as a good starting point to familiarize yourself with the tree APIs
Things not mentioned in the video:
Some "imperative operators" using interfaces like IConnectableObservable, requiring code blocks (and hence statement trees in a fantasy homo-iconic world), are not available on Qbservable. There are just a handful of overloads that fall in this camp such
as the parameterless Publish method (the lambda version is there though).
Qbservable also provides support for join patterns as expression trees. That is, the And and Then operators from System.Joins are available in an expression tree form as well. It's left to the reader to dream up the endless possibilities this enables for
joining multiple data streams "in the cloud".
Constructors like FromAsync which return a Func<T1, ..., Tn, IObservable<TResult>> are supported too. They return a similar Func returning an IQbservable<TResult> instead. That IQbservable will contain an expression tree with an Invoke node upon invoking
the returned function.
Parameters on Qbservable operators are only protected against null reference inputs. That is, no operator-specific enforcements are made. For example, one is allowed to write Take(-5) against an IQbservable, but not against an IObservable. It's up to the
provider to decide what -5 means (take last 5?, don't allow?).
Observable has a static get-only Provider property that exposes the IQbservableProvider that can be used to create expression trees for "local" queries and can be used to get stuff like Return in an expression form, using Observable.Provider.Return(5),
returning an IQbservable<int>. This is complementary to the AsQbservable operator.
Use of AsQbservable on a "local" observable sequence allows one to write expression trees against such a source. When calling Subscribe on the resulting IQbservable, which also implements IObservable per definition, the tree gets rewritten to target the
Observable.* operators, including join patterns and whatnot.
For signatures Qbservable operators, notice that only a minimum set of parameters has been tweaked to be expression-friendly compared to the corresponding Observable operators. That is, things like Amb(IObservable, IObservable) only get the first parameter
as IQbservable. This opens up for quite some flexibility; providers can type-check on the second argument.
Play and amaze us with great observable providers .