Reactive Extensions–More About Chaining

Reactive Programming, Posting # 10

In the previous posting on Reactive Extensions, we created an application that calls onarc of history the Bing translation service to translate a phrase into Japanese, and then back into English.  This allowed the introduction of the SelectMany operator. 

In this posting we’ll ask the Bing Translation service for a list of every language it knows about, and then we’ll translate a phrase into each language in turn.  This will highlight the chaining aspect of SelectMany.  (Click on image to see full size).

To begin, create another WPF application. The Xaml is the same as in the previous example, except that this time we’ll replace the TextBlock with a ListBox, making it easier to see the results.

<TextBox
   Name="Input"
   Text="This is a test"
   Margin="0,0,0,124"
   Grid.RowSpan="2" />
<ListBox
   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" />

 

You’ll need to create the BingTranslator web service reference as you did in the previous example, and the Translate method is identical.  This time we’ll add a new method, ListOfAllLanguages that will return an Observable of an array of strings, each string being a language that Bing knows about. 

private IObservable<string[ ]> 
   ListOfAllLanguages( )
{
   var lsc = new LanguageServiceClient( );
   var allLangs = Observable.FromAsyncPattern<
      string, string[ ]>( 
      lsc.BeginGetLanguagesForTranslate, 
      lsc.EndGetLanguagesForTranslate );
   return allLangs( APP_ID );
}

 

A significant change is how we handle the Click event on the Go button. In this case we want to capture what is Input each time the button is pressed.  We do that by creating a Subject of string named inputText and then assigning to that Subject whatever is in the Text property of the Input TextBlock each time the button is pressed.

private Subject<string> inputText = 
    new Subject<string>( );

public MainWindow( )
{
   InitializeComponent( );
   Go.Click += ( o, e ) => 
     inputText.OnNext( 
       this.Input.Text );

The documentation on Subject states that it represents an object that is both an observable sequence (as any Observable) and also an observer.  In this case, it is observing the input text, and notifying its observers in the call to OnNext which notifies all subscribed observers with the value.

Combining two streams

We want to combine the values observed in the Subject with the values that we’ll be getting back as a result of calling ListOfAllLanguages.  To do that, we use the CombineLatest method of Observable.  The key feature of CombineLatest is that it will give us a value each time either of the observables changes; that is if either the string we’re examining changes or there is a new value returned from ListOfAllLanguages, CombineLatest will return the latest pair.  We can then feed that pair into a struct,

Observable.CombineLatest(
      inputText, ListOfAllLanguages( ),
      ( text, langs ) => new { text, langs } )

 

The result from CombineLatest is a series of IObservables which we now want to recombine so that each element in a new struct will have the phrase and one of the languages.  We can do this with SelectMany and a LINQ statement, the result of which we’ll turn back into yet another Observable,

.SelectMany( 
   x => x.langs.Select( 
   y => new { x.text, lang = y } 
).ToObservable( ) )

 

We can now use SelectMany again to take this Observable and call translate on each member of the structure, asking each to be translated from English to the selected language

.SelectMany( 
   x => Translate( 
   x.text, "en", x.lang ) )

 

We can now chain in a call to ObserveOnDispatcher to marshal the results to the UI thread and then call Subscribe to place the results into the ListBox,

.ObserveOnDispatcher( )
.Subscribe( x => Output.Items.Add( x ) );

 

It is the ability of SelectMany to flatten multiple observable collections into a single Observable that allows this chaining. 

 

Once again, to provide context, here is the complete source file,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Translation3.BingTranslator;

namespace Translation3
{

   public partial class MainWindow : Window
   {
      const string APP_ID = "8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2";

      private Subject<string> inputText = new Subject<string>( );

      public MainWindow( )
      {
         InitializeComponent( );
         Go.Click += ( o, e ) => inputText.OnNext( this.Input.Text );

         Observable.CombineLatest(
               inputText, ListOfAllLanguages( ),
               ( text, langs ) => new { text, langs } )
            .SelectMany( x => x.langs.Select( y => new { x.text, lang = y } ).ToObservable( ) )
            .SelectMany( x => Translate( x.text, "en", x.lang ) )
            .ObserveOnDispatcher( )
            .Subscribe( x => Output.Items.Add( 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 );
      }

      private IObservable<string[ ]> 
         ListOfAllLanguages( )
      {
         var lsc = new LanguageServiceClient( );
         var allLangs = Observable.FromAsyncPattern<
            string, string[ ]>( 
            lsc.BeginGetLanguagesForTranslate, 
            lsc.EndGetLanguagesForTranslate );
         return allLangs( APP_ID );
      }
   }
}

 

For more on SelectMany be sure to read Exercise 8 in the Hands On Lab.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Mini-Tutorial, Reactive and tagged , , , . Bookmark the permalink.

3 Responses to Reactive Extensions–More About Chaining

Comments are closed.