2
votes

This is my code in C#

Observable.Zip(ob1, ob2, (a, b) => a + b);

I am trying to convert this to F# using Pipe-Forwarding operator

ob1 |> Observable.Zip(ob2, Func<_,_,_> (fun a b -> a + b))

It won't compile because it can't get which overload I am trying to use.

Any clue ?

The following works just fine, I am just curious if I could make the pipe-forwarding operator work here. Technically it should take ob1 on left hand side as first parameter and take the two supplied parameters as 2nd and 3rd right ?

 Observable.Zip (ob1,ob2 ,Func<_,_,_>(fun a b -> a + b))
2
just use this (or copy it): github.com/fsprojects/FSharp.Reactive/blob/master/src/… - btw: the problem is that Zip is not a curried function - Random Dev
@CarstenKönig Please consider posting the definition of the zip function from the module as an answer! - Tomas Petricek
why not - I hope I have no error in there (right now on Linux/mono and I somehow cannot get the Rx Nuget right now :( ) - Random Dev

2 Answers

5
votes

As mentioned in the comment you can implement a simple wrapper like this:

    open System.Reactive.Linq

    let zipWith (f : 'T -> 'U -> 'R) 
                (second: IObservable<'U>) 
                (first: IObservable<'T>)
                : IObservable<'R> =
        Observable.Zip(first, second, f)

and with this just do as you wanted:

ob1 |> zipWith (fun a b -> a+b) ob2

PS: it looks even nicer if you do it like this:

ob1 |> zipWith (+) ob2
2
votes

You seem to have some wrong impressions of how pipe operator works, so I'll try to clear those up.

Let's make things simpler to type out and say we have functions foo and bar:

let foo (a, b, f) = f a b
let bar a b f = f a b

foo has more or less the same shape as Observable.Zip and takes a tuple as an argument (this is how C# functions are seen in F#), bar is the same but is curried.

This works:

foo (ob1, ob2, fun a b -> a + b)
bar ob1 ob2 (fun a b -> a + b)

This doesn't work:

ob1 |> bar ob2 (fun a b -> a + b)

That's because what pipe operator does, is taking the value on the left, and passing it as the last argument to the function on the right. You'd need bar to be defined like this:

let bar b f a = f a b

That's why the functions in for example the List module are defined in a way that the actual list is passed in as the last argument - that makes pipelining work in a nice way.

This also doesn't work:

ob1 |> foo (ob2, fun a b -> a + b)

Apart from the previous problem, it would also require pipe operator to look inside a tuple and attach a value there, and that's really not how it works. A tuple is a single value in F#. The function that would work for that example would be this one:

let foo (b, f) a = f a b

But clearly that's not what we want.

You can still use Observable.Zip in a pipeline fashion like this:

ob1 |> fun x -> Observable.Zip(x, ob2, Func<_,_,_> (fun a b -> a + b))

Or simply go with the wrapper the other answer suggests.