Hands-On Reactive Programming with Clojure
上QQ阅读APP看书,第一时间看更新

One more flatmap for the road

You might be wondering what would happen if the observable sequence we're flatmapping emitted more than one value. What then?

We'll look at one last example before we begin the next section so that we can illustrate the behavior of flatMap in such cases.

Here's an observable that emits its argument twice:

(defn repeat-obs [n] 
  (rx/seq->o (repeat 2 n))) 

Using it is straightforward, as demonstrated in the following code block:

(-> (repeat-obs 5) 
    (rx/subscribe prn-to-repl)) 
 
;; 5 
;; 5 

As we did previously, we'll now combine this observable with the one we created earlier, all-positive-integers. Before reading on, think about what you expect the output to be for, say, the first three positive integers.

The code is as follows:

(rx/subscribe (->> (all-positive-integers) 
                   (rx/flatmap repeat-obs) 
                   (rx/take 6)) 
              prn-to-repl) 

The output is as follows:

    0
    0
    1
    1
    2
    2  

The result might be unexpected for some readers. Let's have a look at the marble diagram for this example and make sure we understand how it works:

Each time repeat-obs gets called, it emits two values and terminates. flatmap then combines them all in a single observable, making the previous output clearer.

One last thing worth mentioning about flatmap—and the title of this section—is that its friends refer to the several names by which flatmap is known.

For instance, Rx.NET calls it selectMany. RxJava and Scala call it flatMap, though RxJava has an alias for it—mapMany. The Haskell Community calls it bind. Though they have different names, these function's semantics are the same and are part of a higher-order abstraction called a Monad. We don't need to know anything about Monads to proceed.

The important thing to keep in mind is that when you're sitting at the bar talking to your friends about Compositional Event Systems (CES), all these names mean the same thing.