Comments

@Herman van der Blom: Rx v2.0 won't be part of the Windows Phone 8 SDK, but the existing Microsoft.Phone.Reactive will still be supported in order for users of WP7 to migrate to WP8. We will release an Rx v2.0 build with Windows Phone 8 support though, also based on Portable Library to allow sharing between .NET 4.5, .NET 4.5 for Windows Store apps, and Windows Phone 8.

In order to use ForkJoin, you'll have to add a reference to the experimental Rx assembly, but it should continue to work as it did before. Alternatively, if this is single-value async, you could also use Task.WaitAll or Observable.CombineLatest to achieve a similar effect, without having to rely on the experimental functionality.

@dcuccia: In fact, the hardest part was working with a moving target, i.e. .NET Framework 4.5 Beta, RC, and internal interim builds, while developing Rx v2.0. If we'd only start now, with the final bits of .NET 4.5 and Portable Library support, it wouldn't be as hard.

The main thing to decide is what you want to do with platform-specific functionality that ended up in a bottom layer of your library: keep the API surface there, discover it at runtime, and mark it deprecated to stimulate new usage patterns; or have a clean slate release for Portable Library with migration guidance for the types/members that moves. (In fact, if you don't have to slice a type because it had both platform neutral and platform-specific stuff in it, you can simply move the type up the layer map.) As explained, we went with the second approach initially, but the benefits diminished as we went along, simply because Portable Library's intersection between our target platforms got much better by RTM.

If I'd be doing it again now, I'd really strive for a portable core that eliminates platform-specific copies right from the start. Based on what falls out of the intersection, I'd decide whether some kind of enlightenment mechanism is required / warranted or not.

To implement such an enlightenment mechanism, the main trick is Type.GetType(string, bool) to look for a known type in a known assembly (ideally demanding an exact version match, saving you from future headaches) implementing a known interface. Then decide whether you can do a graceful fallback if the module is missing. At the same time, try making all deployment vehicles such that the enlightenment assembly is always installed (e.g. as part of a NuGet package, or in an Extension SDK).

I'll chat with the Portable Library team about documentation and guidance plans.

@Thorium: The only new implicit thing for FromEvent* usage is an implicit SubscribeOn based on the SynchronizationContext.Current captured at the point of query construction. In other words, only the side-effects of subscribing (+= on the event) and disposing the subscription (-= on the event) are marshaled to the right context, in order to avoid issues for thread-affine events (e.g. in Windows XAML for Metro Style apps). There's no implicit ObserveOn behavior, hence all existing guidance to insert this explicitly stays the same.

@Maddus Mattus: No need for extremism here . Just like methods can be turned into first class objects using delegates, events can be turned into first class objects using observable sequences.

The analogy extends to a comparison of advantages and disadvantages between both approaches. For example, objects have a cost that may not be warranted for a lot of simple events (e.g. button clicks). Also, events are well-suited for a code-centric usage (e.g. kicking off a simple action), while observable sequences provide for a data-centric approach (e.g. computing statistics over temporal data).

@LeeCampbell: At this point, there are no magic tricks to know whether an operator is part of a "safe" chain. What's really going on here is a different approach to writing operators, based on custom observer implementations rather than the anonymous implementation approach through lambda-based Subscribe calls to the sources of the operator.

In order to ensure "global" safety, there's a minimal amount of cheap safeguarding based on swapping out observers for mute ones as soon as an operator's input reaches a terminal state. So, multiple terminal messages (invalid from the grammar's point of view) are silenced but the first one, and the place this happens is inside the custom operator noticing this (rather than in the Subscribe method's wrapping of the observer in the past).

As for concurrent notifications (which are invalid too), there has never been true safeguarding against this, so inputs are assumed to be safe. Only when an operator has to deal with multiple sources, it will do proper orchestration internally (e.g. Merge has to ensure we never talk to the outgoing observer in a concurrent manner), using locks or other approaches. However, when an ill-behaved producer is in the mix, behavior is undefined (e.g. a Subject<T> that exhibits concurrent messages). In case such a producer exists, one has to safeguard the pipeline using Synchronize.

When implementing sources or custom operators, we recommend to use ObservableBase<T> and Observable.Create, respectively. Those are the safest way to get things right. In fact, for custom operators, the use of composition of existing operators is a good start. Going forward, we may provide more advanced "power user" facilities to write more efficient custom operators using techniques similar to the ones we're using internally now. However, with power comes responsibility, so users of such primitives and implementation patterns need to have a thorough understanding of expectations with regards to observable sequences, the observer grammar, etc.

@Kim: The platform enhancements are loaded through a Type.GetType call, followed by an instantiation of the discovered type (if any). The resulting object is cached so subsequent uses of the service don't take a performance hit.

The BCL/CLR teams are aware of the need for such tricks in some types of APIs, and we may see more guidance or helper functionality to address this going forward (no promises implied in this statement). For a lot of Portable Libraries though, one can live without such mechanisms. Think about class libraries for a data model that can be reused across Windows Phone, Metro, Silverlight, classic desktop, etc.

@Maddus Mattus: Still need to allocate the IDisposable. This said, not that big of a deal, especially if you don't ever need to unsubscribe, meaning the object becomes food for a Gen0 collection.

As soon as you start composing events, the benefits far outweigh the cost. A lot of the composition of Rx builds on the ability to compose subscriptions. Subscribing to the merge of N sources means returning an IDisposable that holds on to the underlying N source subscriptions. It's as simple as that.

If you'd measure all sorts of performance metrics in real-world scenarios, I doubt the disposable object graph will show up as a relevant influence. (Btw, we have many IDisposable implementations internally in Rx, also to allow for reuse of objects.) It definitely didn't in the Rx v2.0 Beta analysis of performance. Also, typically there's 1 subscription for a whole bunch of events flowing through the pipeline: 1 <<<<< N.

To conclude, my remark is mostly about the debate whether or not observables are a primitive or an abstraction over a primitive. Just like delegates are first-class representations of methods, observables are first-class representations of events (which are method pairs for our purposes). In this analogy, one doesn't "convert" (e.g. through method group conversion in C#, see ECMA 334, section 13.6) a method into a delegate unless you want to do a more functional style composition (e.g. LINQ to Objects heavily relies on delegates). The same holds for observables, with the only difference being a bit more friction to convert an event to its first-class representation (i.e. there's no method group equivalent for event members).

@Novox: You're seeing an internal namespace with a Greek alpha in its name to make the call stack look exactly like the methods you wrote. We can't have inner classes such as Where nested inside Observable, because there's already a method with that name. So, the closest approximation was an Observαble namespace with classes such as Where in it.

Unfortunately, the namespace shows up in IntelliSense at this point, due to some complexities around the IDE's handling of InternalsVisibleTo. We're aware of this problem and are looking into it.

@Maddus Mattus: Observables come at a slightly higher cost than events, e.g. due to the need for an IDisposable "subscription handle". So, having events at the core isn't a bad thing either. For "non-complex event processing" scenarios, a simple event handler often suffices (e.g. the form closing event to pop up a "do you want to save" dialog).

What you likely want is a smoother way to convert an event into an observable sequence, just like F# has an implicit conversion allowing this (see here and here for starters).