Toying around with F# Queries, Rx, Portables Libraries, Windows [Phone] 8 and the Zip operator

By jay at January 02, 2013 22:42 Tags: , , ,

Agreed, that’s a lot of keywords. Yet, they fit one another in a very interesting way.

I find more and more that F#, particularly regarding the way my development trends are going, is getting more and more of a fit regarding the immutability and flexibility needs.

Given that, I thought I’d give a try at running some F# Query Expressions using custom Rx operators, on Windows Phone 8, using Portable Libraries.

Getting F# to run on Windows Phone 8

Interestingly, the F# team made a portable library version of their runtime, so that it can run under the intersection of .NET 4.5, Windows Store Apps and Silverlight 5 frameworks. Windows Phone 8 projects accept this intersection, which make F# available to work with.

That was easy.

 

Getting Rx to run on F#’s PLib intersection

That one is a bit more tricky. Out of the box, Rx supports a different PLib frameworks intersection, being .NET 4.5 and Windows Store Apps.

Fortunately, the Rx team published the source code of the library, making this experiment a lot easier to do.

I’ve updated a bit the Rx sources to match a set of new defines :

<DefineConstants>
$(DefineConstants);NO_RXINTERFACES;PREFER_ASYNC;
HAS_APTCA;NO_HASHSET;NO_CDS;NO_SEMAPHORE;NO_STOPWATCH;
NO_REMOTING;NO_SERIALIZABLE;NO_THREAD;PLIB
</DefineConstants>

I’ve also changed the PLib profile from Profile7 and Profile47, to match the same profile as the F# portable library. There’s actually a new define I introduced, NO_SEMAPHORE, because that new profile does not support the SemaphoreSlim class.

I had to make a small update to the code to make that code Semaphore-less, if you’re interested let me know, I’ll share that.

 

Playing with the F# Query Expressions and Rx

I’ve come across an interesting article from Marcin Najder about F# 3.0 and Query Expressions using the Reactive Extensions and this got me started.

The interesting side is to be able to have comprehensions that simplify the writing of queries that contain LINQ or Rx operators that may not be supported by C#, such as IObservable.Take() :

type Dummy() = 
 
        let rxquery = new RxQueryBuiler()
        let obs = new ObsBuiler()
 
        let values =
            obs {
                for value in [0..15] do
                    yield value
            }
           
        member this.Foo = 
            rxquery {
                for value in values do
                where (value > 1)
                select value
                take 10 into trimmed
                select trimmed 
            }
    

The Foo property can be used directly from a C# program, and observed like any other IObservable instance.

The ZipLike custom operator

With Rx and their enumerable counterparts (such as Linq), the Zip operator allows to get two streams of values and produce another using each tuple of values.

The problem with this operator is that in C# it cannot be written using the “from xx in yy” LINQ syntax, because there is no keyword for it, and the language does not allow the creation of new ones (though some are trying to get that into future C# versions).

There are no examples or documentation anywhere to use a custom Zip operator (using the CustomOperation.IsLikeZip property), so digging a bit into the F# test suite got me started on how to use it.

So, instead of writing a Zip like this :

o1.Zip(o2, (lv, rv) => new Tuple(lv, rv));
 

It can be written like this in F#, by defining a custom zip operator :

    let values2 =
        obs {
            for value in [5..20] do
                yield value
        }
    member this.Combined = 
        rxquery {
            for test in values do
            zip test2 in values2
            select (test, test2)
        }
 

This returns a simple tuple for the combined values.

But this also has the advantage of providing what the C# let keyword does, by creating query variables :

        member this.Combined = 
            rxquery {
                for test in values do
                zip test2 in values2
                let values = (test * 2, test2)
                where (fst(values) > 5)
                select (test, test2)
            }
    

There is no need to create temporary anonymous types to forward the state of the query to downstream Rx operators manually.

Cool stuff, really.

blog comments powered by Disqus

About me

My name is Jerome Laban, I am a Software Architect, C# MVP and .NET enthustiast from Montréal, QC. You will find my blog on this site, where I'm adding my thoughts on current events, or the things I'm working on, such as the Remote Control for Windows Phone.