I’d like to augment
public static IObservable<TSource> Create<TSource>(
Func<IObserver<TSource>, Action> subscribe)
{...}
For use in F# so that rather than calling with a Function or Action I can just use standard F# types i.e IObserver -> (unit -> unit).
How can I accomplish this?
Edit:
Adding full example. Not sure why obsAction does not work.
open System
open System.Reactive
open System.Reactive.Disposables
open System.Reactive.Linq
type Observable with
static member Create(subscribe) =
Observable.Create(fun observer -> Action(subscribe observer))
let obsDispose (observer:IObserver<_>) =
let timer = new System.Timers.Timer()
timer.Interval <- 1000.00
let handlerTick = new Timers.ElapsedEventHandler(fun sender args -> observer.OnNext("tick"))
let handlerElapse = new Timers.ElapsedEventHandler(fun sender args -> printfn "%A" args.SignalTime)
timer.Elapsed.AddHandler(handlerTick)
timer.Elapsed.AddHandler(handlerElapse)
timer.Start()
Disposable.Empty
let obsAction (observer:IObserver<_>) =
let timer = new System.Timers.Timer()
timer.Interval <- 1000.00
let handlerTick = new Timers.ElapsedEventHandler(fun sender args -> observer.OnNext("tick"))
let handlerElapse = new Timers.ElapsedEventHandler(fun sender args -> printfn "%A" args.SignalTime)
timer.Elapsed.AddHandler(handlerTick)
timer.Elapsed.AddHandler(handlerElapse)
timer.Start()
let action() =
timer.Elapsed.RemoveHandler(handlerTick)
timer.Elapsed.RemoveHandler(handlerElapse)
timer.Dispose()
action
let obsOtherAction (observer:IObserver<_>) =
let timer = new System.Timers.Timer()
timer.Interval <- 1000.00
let handlerTick = new Timers.ElapsedEventHandler(fun sender args -> observer.OnNext("tick"))
let handlerElapse = new Timers.ElapsedEventHandler(fun sender args -> printfn "%A" args.SignalTime)
timer.Elapsed.AddHandler(handlerTick)
timer.Elapsed.AddHandler(handlerElapse)
timer.Start()
new System.Action( fun () ->
timer.Elapsed.RemoveHandler(handlerTick)
timer.Elapsed.RemoveHandler(handlerElapse)
timer.Dispose())
let worksNeverStops = obsDispose |> Observable.Create |> Observable.subscribe(fun time -> printfn "Time: %A" time)
let actionWorks = obsOtherAction |> Observable.Create |> Observable.subscribe(fun time -> printfn "Time: %A" time)
let doesNotWork = obsAction |> Observable.Create |> Observable.subscribe(fun time -> printfn "Time: %A" time)
The problem you’re facing is an FP gotcha.
In,
the type of
subscribeisIObserver<_> -> unit -> unit.Now there’s a subtle difference between
IObserver<_> -> unit -> unitand
IObserver<_> -> ActionwhereAction : unit -> unit. The difference is that the former is curried, and the latter isn’t.When an observer subscribes,
subscribe observerreturns a method in to which()can be applied to getunit– your subscribe method will never actually be called until the last()is applied – which won’t be until it un-subscribes by which point it will already be detached.You can get over it by forcing it to not be curried:
Further:
If you check the IL, the equivalent VB (function refs are more clearer in VB) versions for the
Invokefor theFastFuncgenerated foris:
and for:
is:
AddressOf New Closure(Me.subscribe, arg).Invoke-> The subscribe function won’t get called until the dispose action is called.AddressOf New Closure(Me.subscribe.Invoke(arg)).Invoke-> The subscribe function actually gets called and the resulting action is returned as expected.I hope it is now clear why the second case works, and not the first.