Reactive Programming, Posting # 9
Reactive Extensions (Rx) has a number of operators that we’ve looked at already, including Select, Throttle, Subscribe and so forth. While SelectMany is not the most important Rx operator, it is surely the most powerful.
SelectMany’s principal job is to allow you to select against an IObservable<T>, run a func<u> and get back an IObservable<u>
IObservable<U> SelectMany(IObservable<T>, func<U>)
Read that in your head “SelectMany takes an IObservable of T and a func (funk) of U and returns an IObservable of U.” |
Today we’ll take a look at the case of having a function that returns an IObservable and you wish to then pass the results to a function that returns an IObservable. That is, the case where you wan to chain calls. If you were to use Select, you would end up with an IObservable<IObservable<T>> which is not what you want. SelectMany flattens that for you.
To see this at work, create a new WPF application (though the same can be done with Silverlight and with the Phone). In this application we will pass a phrase to BingTranslator for translation into Japanese, and then we’ll send it back to BingTranslator for translation back into English.
To do this, you’ll need to get a Bing API key.
Begin by creating a very simple UI consisting of a TextBox (to take the phrase we want to translate) and a TextBlock (to display the results) and a button (to set off the action):
<Window x:Class="Translation2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="36*" /> <RowDefinition Height="242*" /> <RowDefinition Height="33*" /> </Grid.RowDefinitions> <TextBox Name="Input" Text="This is a test" Margin="0,0,0,124" Grid.RowSpan="2" /> <TextBox TextWrapping="Wrap" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="10,17,10,10" Name="Output" Grid.Row="1" /> <Button Name="Go" Content="Go" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Window>
In the code-behind, create a constant for your API key,
const string APP_ID = "6xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxC";
Create an event handler and in that event handler you’ll invoke a private helper method Translate that will take your input text, your input language and your output language,
public MainWindow( ) { InitializeComponent( ); Go.Click += new RoutedEventHandler( Go_Click ); } private void Go_Click( object sender, RoutedEventArgs e ) { Translate( this.Input.Text, "en", "ja" )
Create a reference to the Bing Translation service at http://api.microsofttranslator.com/V2/Soap.svc
Enter the URL [1], click the Go button [2], enter a name for the service [3] and be sure to click on the advanced button [4] so that you can check Generate asynchronous operations [5]
Use the object browser to examine the myriad methods for translation. What we want in this case is to instantiate a Language Service,
private IObservable<string> Translate( string text, string from, string to ) { var lsc = new LanguageServiceClient( ) as LanguageService;
We can then use the language service to call the asynchronous BeginTranslate and EndTranslate, or, better, we can use Rx’s FromAsyncPattern to simplify the asynchronous call. To do so, we examine the non-asynchronous version to see how many parameters it has (six strings) and to find its return value (a string).
At the moment, the phone version does not allow you to create a func<6 params> – it is limited to 4, which is why we are doing this in WPF. |
var myFunc = Observable.FromAsyncPattern< string, string, string, string, string, string, string>( lsc.BeginTranslate, lsc.EndTranslate );
We can invoke that function by pasing in the APP_ID we created above, the text we want to translate, the language we want to translate from and the language we want to translate to. The final two parameters can be null,
return myFunc( APP_ID, text, from, to, null, null ); }
We invoke Translate by passing in the text and the two letter acronym used to identify the original and translated languages
Translate( this.Input.Text, "en", "ja" )
As you can see from the return type of our Translate method, it returns an IObservable<String>. I can’t use select to then send the results back to Translate or I’ll end up with an IObservable of IObservable. This is where SelectMany does its magic,
.SelectMany( x => Translate( x, "ja", "en" ) )
The result is an IObservable<String> which I can then subscribe to. Here is the complete source code,
using System; using System.Linq; using System.Windows; using Translation2.BingTranslator; namespace Translation2 { public partial class MainWindow : Window { const string APP_ID = "6DC862858476B72127BC381A1EAADABBC281C88C"; public MainWindow( ) { InitializeComponent( ); Go.Click += new RoutedEventHandler( Go_Click ); } private void Go_Click( object sender, RoutedEventArgs e ) { Translate( this.Input.Text, "en", "ja" ) .SelectMany( x => Translate( x, "ja", "en" ) ) .ObserveOnDispatcher( ) .Subscribe( x => Output.Text = x ); } private IObservable<string> Translate( string text, string from, string to ) { var lsc = new LanguageServiceClient( ) as LanguageService; var myFunc = Observable.FromAsyncPattern< string, string, string, string, string, string, string>( lsc.BeginTranslate, lsc.EndTranslate ); return myFunc( APP_ID, text, from, to, null, null ); } } }
In the next example, we’ll build on this and create an application that translates a single phrase into multiple languages.
I have noticed you don’t monetize your site, don’t waste your traffic, you
can earn extra bucks every month because you’ve got hi quality content.
If you want to know how to make extra bucks, search for: Mrdalekjd methods
for $$$
My partner and i cherished your blog publish. Actually looking towards understand far more. Wonderful.
@Shai,
The TPL is not part of Silverlight, therefore, it’s not part of WP7 Silverlight. That said, there has been a lot of demand from the community for TPL as part of Silverlight, and it’s likely that in April, at the Mix 11 conference, Microsoft will release a beta of Silverlight 5, which will include TPL.
In the meantime, you *can* do chain async calls without the TPL using Rx. Rx’s version, which will work on SL 4 and WP7: .SelectMany
Or, if you prefer the query syntax:
var firstComputation = Observable.FromAsyncPattern(…);
var secondComputation = Observable.FromAsyncPattern(…)
var observableResult = from result in firstComputation.Invoke(…)
from otherResult in secondComputation.Invoke(…)
select otherResult;
I created a google voice client app called G-mobilevoice. I am rewritting in MVVM, but wondering about using TPL for setting up continuation tasks in wp7 like:
var t = Task.Factory.StartNew(() => 54);
var c = t.ContinueWith((antecedent) =>
{
//Do something},
TaskContinuationOptions.OnlyOnRanToCompletion);
is this part fo the microsoft.phone.reactive or threadingnamespaces?
I need to chain multiple async calls to google unfortunately to login.
The restriction of 6, 4, etc. on the Func really isn’t a problem – that’s when you can just create a struct:
public struct MyFunctionValue
{
string Field1;
string Field2;
}
etc. and just make it a Func for it to work fine on the phone!