You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Part 3 - Taming the sequence/2. Leaving the monad.md
+21-22Lines changed: 21 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,13 +1,13 @@
1
1
# Leaving the monad
2
2
3
-
A monad is an abstract concept from functional programming that is unfamiliar to most programmers. It is beyond the scope of this guide to teach monads in general. To quote www.introtorx.com
3
+
A monad is an abstract concept from functional programming that is unfamiliar to most programmers. It is beyond the scope of this guide to about teach monads in general. In www.introtorx.com we find a short definition:
4
4
> Monads are a kind of abstract data type constructor that encapsulate program logic instead of data in the domain model.
5
5
6
-
It is interesting to understand that Rx, and more specifically the observable, is a monad. Rx code declares what needs to be done but the actual processing happens not when Rx statements are executed, but when values are emitted. Readers may find it interesting to read more about monads in general. For this guide, when refering to the monad the reader only needs to think about the observable.
6
+
Monads are of interest to us, because Rx, and more specifically the observable, is a monad. Rx code declares what needs to be done but the actual processing happens not when Rx statements are executed, but rather when values are emitted. Readers may find it interesting to read more about monads in general. For this guide, when refering to monads the reader only needs to think about the observable.
7
7
8
8
## Why leave the monad
9
9
10
-
There are two main reasons one may want to leave the monad. The first reason is that a new Rx developer will still be more comfortable in the more traditional paradigms. Doing part of the computation in a familiar style may make code easier to understand. Another reason is to interact with components and libraries that weren't designed with Rx in mind. When refactoring existing code into Rx, it may be useful to have Rx behave in a blocking way.
10
+
There are two main reasons one may want to leave the monad. The first reason is that a new Rx developer will still be more comfortable in more traditional paradigms. Doing parts of the computation in a different paradigm may enable you to get some parts working, while you're still figuring out how to do things in Rx. Another reason to leave the monad is to interact with components and libraries that weren't designed with Rx in mind. When refactoring existing code into Rx, it may be useful to have Rx behave in a blocking way.
11
11
12
12
## BlockingObservable
13
13
@@ -21,7 +21,7 @@ or the static factory of `BlockingObservable`
`BlockingObservable` does not extend the `Observable` and it can't be used with our usual Rx operators. It has its own implementations of a small set functions that allow you to extract data out of an `Observable` in a blocking manner. Many of those methods are the blocking counterparts to methods that we have already seen.
24
+
`BlockingObservable` does not extend the `Observable` and it can't be used with our usual Rx operators. It has its own implementations of a small set functions, which allow you to extract data out of an `Observable` in a blocking manner. Many of those methods are the blocking counterparts to methods that we have already seen.
The code here behaves like `subscribe` would. First you register an observer (no overload for `forEach` accepts `Observer` but the semantics are the same). Execution then proceeds to print "Subscribed" and exits our snippet. As values are emitted (the first one with a 100ms delay), they are passed to our observer for processing.
50
+
The code here behaves like `subscribe` would. First you register an observer (no overload for `forEach` accepts `Observer`, but the semantics are the same). Execution then proceeds to print "Subscribed" and exits our snippet. As values are emitted (the first one with a 100ms delay), they are passed to our observer for processing.
52
51
53
-
`BlockingObservable` doesn't have a `subscribe` function, but it has `forEach`. Let see the same example with `BLockingObservable`
52
+
`BlockingObservable` doesn't have a `subscribe` function, but it has `forEach`. Lets see the same example with `BLockingObservable`
We see here that the call to `forEach` blocked until the observable completed. Another difference is that there can be no handlers for `onError` and `onCompleted`. `onCompleted` is a given if the execution continues and exceptions are thrown into the runtime to be caught:
73
+
We see here that the call to `forEach` blocked until the observable completed. Another difference is that there can be no handlers for `onError` and `onCompleted`. `onCompleted` is a given if the execution continues, while exceptions will be thrown into the runtime to be caught:
`BlockingObservable` has methods for `first`, `last` and `single`, along with implementations for default values `firstOrDefault`, `lastOrDefault` and `singleOrDefault`. Having read about their namesakes in `Observable`, you already know the returned value is defined. Once the again, the difference in the blocking nature of the methods. They don't return an observable that will the value when it is available. Rather, they block until the value is available and return the value itself, without the surrounding observable.
97
+
`BlockingObservable` has methods for `first`, `last` and `single`, along with implementations for default values `firstOrDefault`, `lastOrDefault` and `singleOrDefault`. Having read about their [namesakes](/Part%202%20-%20Sequence%20Basics/4.%20Aggregation.md#first) in `Observable`, you already know what the returned value is. Once again, the difference is the blocking nature of the methods. They don't return an observable that will emit the value when it is available. Rather, they block until the value is available and return the value itself, without the surrounding observable.
@@ -138,7 +135,7 @@ Caught: java.lang.IllegalArgumentException: Sequence contains too many elements
138
135
139
136
### To Iterable
140
137
141
-
You can transform your observables to [iterables](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) throught a variety of methods on `BlockingObservable`. Iterables are pull-based, unlike Rx which is push-based. That means that when the consumer is ready to consume a value, one is requested with `next()` method on the iterable's [Iterator](https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html). The call to `next()` will either return a value immediately or block until one is ready.
138
+
You can transform your observables to [iterables](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) throught a variety of methods on `BlockingObservable`. Iterables are pull-based, unlike Rx, which is push-based. That means that when the consumer is ready to consume a value, one is requested with `next()` on the iterable's [Iterator](https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html). The call to `next()` will either return a value immediately or block until one is ready.
142
139
143
140
There are several ways to go from `BlockingObservable<T>` to `Iterable<T>` and each has a different behaviour.
144
141
@@ -149,7 +146,7 @@ public java.lang.Iterable<T> toIterable()
In this implementation, all the emitted values are collected and cached. Because of the caching, no items will be missed. The iterator gets the next value immediately or blocks until the next value becomes available.
149
+
In this implementation, all the emitted values are collected and cached. Because of the caching, no items will be missed. The iterator gets the next value as soon as possible, either immediately if it has already occured, or it blocks until the next value becomes available.
@@ -212,7 +209,7 @@ In this example the consumer is slower than the producer and always misses the n
212
209
publicjava.lang.Iterable<T> latest()
213
210
```
214
211
215
-
The `latest` method is similar to `next`, with the difference that it will cache one value. The iterator only blocks if no events have been emitted by the observable since the last value consumed. As long as there has been a single event, the iterator will return immediately with a value, or with the termination of the iteration.
212
+
The `latest` method is similar to `next`, with the difference that it will cache one value. The iterator only blocks if no events have been emitted by the observable since the last value was consumed. As long as there has been a new event, the iterator will return immediately with a value, or with the termination of the iteration.
When using the `latest` iterator, values will be skipped if they are not pulled before the next event is emitted. If the consumer is faster than the producer, the iterator will block in wait for the next value.
238
+
When using the `latest` iterator, values will be skipped if they are not pulled before the next event is emitted. If the consumer is faster than the producer, the iterator will block and wait for the next value.
242
239
243
240
It is interesting here that 4 was never consumed. That was because an `onCompleted` followed immediately, resulting in the next pull seeing a terminated observable. The implicit `iterator.hasNext()` method reports a terminated observable without checking if the last value has been consumed.
244
241
@@ -249,7 +246,7 @@ public java.lang.Iterable<T> mostRecent(T initialValue)
The `mostRecent` iterator never blocks. It caches a single value, therefore values may be skipped is the consumer is slow. Unlike `latest`, the last cached value is always returned, resulting in repetitions if the consumer is faster than the producer. To allow the `mostRecent` iterator to be completely non-blocking, an initial value is needed. That value is returned if the observable has not emitted any values yet.
249
+
The `mostRecent` iterator never blocks. It caches a single value, therefore values may be skipped if the consumer is slow. Unlike `latest`, the last cached value is always returned, resulting in repetitions if the consumer is faster than the producer. To allow the `mostRecent` iterator to be completely non-blocking, an initial value is needed. That value is returned if the observable has not emitted any values yet.
A `BlockingObservable<T>` can be presented as a [Future<T>](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html) using the `toFuture` method. This method only creates an instance of `Future` and does not block. Execution blocks as necessary when getting the value. `Future` allows the consumer to decide how to approach an asynchronous operation. `Future`are also capable of reporting errors in the operation.
281
+
A `BlockingObservable<T>` can be presented as a [Future<T>](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html) using the `toFuture` method. This method only creates an instance of `Future` and does not block. Execution blocks as necessary when getting the value. `Future` allows the consumer to decide how to approach an asynchronous operation. A `Future`is also capable of reporting errors in the operation.
So far we were able to ignore potential deadlocks. Rx's non-blocking nature makes it harder to create unnecessary deadlocks. However, in this chapter we returned to blocking methods, thus bringing deadlocks again to the forefront.
304
+
So far we were able to ignore potential deadlocks. Rx's non-blocking nature makes it harder to create unnecessary deadlocks. However, in this chapter we returned to blocking methods, thus bringing deadlocks to the forefront again.
308
305
309
306
The example below would work as a non-blocking case. But because we used blocking operations, it will never unblock
310
307
@@ -317,9 +314,11 @@ subject.onNext(2);
317
314
subject.onCompleted();
318
315
```
319
316
317
+
`forEach` returns only after the termination of the sequence. However, the termination event requires `forEach` to return before being pushed. Therefore, `forEach` will never unblock.
318
+
320
319
### Non-terminating sequences
321
320
322
-
Some blocking ways to access observables, such as `last()`, require the observable to terminate to unblock. Others, like `first()`, require it to emit at least one event to unblock. Using those methods on `Observable` isn't a big danger, as they only return an non-terminating observable. These same `methods` on `BlockingObservable` can result in a permanent block if the consumer hasn't taken the time to enforce some guarantees, such as timeouts (we will see how this is done in [Timeshifter sequences](/Part 3 - Taming the sequence/5. Time-shifted sequences.md)).
321
+
Some blocking ways to access observables, such as `last()`, require the observable to terminate to unblock. Others, like `first()`, require it to emit at least one event to unblock. Using those methods on `Observable` isn't a big danger, as they only return a non-terminating observable. These same `methods` on `BlockingObservable` can result in a permanent block if the consumer hasn't taken the time to enforce some guarantees, such as timeouts (we will see how this is done in [Timeshifter sequences](/Part 3 - Taming the sequence/5. Time-shifted sequences.md)).
0 commit comments