3

I hit something new to me with the following piece of code when following the equivalent in C# here. The compiler gives multiple errors basically telling the IConnectableObservable created in source.Publish() does not match IObservable even though it derives from it (according to the MSDN article linked).

Is there something in F# that is different with regard to C# concerning inheritance in this case or can someone provider pointers as to what is going on? Have I just made a typo I can't see? What comes to the heading regarding covariance, it's just a wild guess as I'm at least temporarily out of ideas. And so, maybe writing somewhere may help me and others...

One example of the many error messages:

No overloads match for method 'Create'. The available overloads are shown below (or in the Error List window).

No overloads match for method 'Switch'. The available overloads are shown below (or in the Error List window).

Error Possible overload: '(extension) IObservable.Switch<'TSource>() : IObservable<'TSource>'. Type constraint mismatch. The type IObservable<IConnectableObservable<'b>> is not compatible with type IObservable<IObservable<'a>> The type 'IObservable<'a>' does not match the type 'IConnectableObservable<'b>'.

open System.Reactive.Concurrency
open System.Reactive.Disposables
open System.Reactive.Subjects
open System.Reactive.Linq


type Observable with
    static member inline Suspendable(source: IObservable<_>, suspend: IObservable<bool>, isSuspendedInitially: bool): IObservable<_> =
        Observable.Create<_>(fun observer ->
            let shared = source.Publish()
            let pausable =
                suspend.StartWith(isSuspendedInitially)
                    .TakeUntil(shared.LastOrDefaultAsync())
                    .DistinctUntilChanged()
                    .Select(fun p -> if p then shared else Observable.Empty<_>())
                    .Switch()
            new CompositeDisposable(pausable.Subscribe(observer), shared.Connect()))

The corresponding C# code

public static class RxExtensions
{
    public static IObservable<T> Suspendable<T>(this IObservable<T> stream, IObservable<bool> suspend, bool isSuspendedInitially)
    {            
        return Observable.Create<T>(o =>
        {
            var shared = stream.Publish();
            var pausable = suspend
                .StartWith(isSuspendedInitially)
                .TakeUntil(shared.LastOrDefaultAsync())
                .DistinctUntilChanged()
                .Select(p => p ? shared : Observable.Empty<T>())
                .Switch();
            return new CompositeDisposable(pausable.Subscribe(o), shared.Connect());                
        });
    }
}
Community
  • 1
  • 1
Veksi
  • 3,556
  • 3
  • 30
  • 69

1 Answers1

3

This was a bit tricky, but you need to add two upcasts: shared to IObservable<_>, and the result of the lambda function to IDisposable. These would be implicit in C#, but need to be explicit in F#:

type Observable with
    static member inline Suspendable (source: IObservable<_>, 
                                      suspend: IObservable<bool>, 
                                      isSuspendedInitially: bool): IObservable<'a> =
        Observable.Create<_>(fun observer ->
            let shared = source.Publish()
            let pausable = 
                suspend.StartWith(isSuspendedInitially)
                    .TakeUntil(shared.LastOrDefaultAsync())
                    .DistinctUntilChanged()
                    .Select(fun p -> if p then shared :> IObservable<_> 
                                          else Observable.Empty<_>())
                    .Switch()
            new CompositeDisposable(pausable.Subscribe(observer), 
                                    shared.Connect()) :> IDisposable)
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Christopher Stevenson
  • 2,843
  • 20
  • 25
  • 1
    Ah, that was it then! I think I missed the cast to IDisposable as I think I tried a cast to IObservable (well, late at night etc.). One lives and learns. I can't check this before the evening, but this must be it! With a quick search I found [How to implicitly convert to common super types in F# pattern matches?](http://stackoverflow.com/questions/3909359/how-to-implicitly-convert-to-common-super-types-in-f-pattern-matches), which brings additional aspects and techniques to handle this situation. I believe it could have wider interest. – Veksi Oct 28 '14 at 07:21