|
| 1 | +RxScala Release Notes |
| 2 | +===================== |
| 3 | + |
| 4 | +This release of the RxScala bindings builds on the previous 0.15 release to make the Rx bindings for Scala |
| 5 | +include all Rx types. In particular this release focuses on fleshing out the bindings for the `Subject` and `Scheduler` |
| 6 | +types, as well as aligning the constructor functions for `Observable` with those in the RxJava. |
| 7 | + |
| 8 | +Expect to see ongoing additions to make the Scala binding match the equivalent underlying Java API, |
| 9 | +as well as minor changes in the existing API as we keep fine-tuning the experience on our way to a V1.0 release. |
| 10 | + |
| 11 | +Observer |
| 12 | +-------- |
| 13 | + |
| 14 | +In this release we have made the `asJavaObserver` property in `Observable[T]`as well the the factory method in the |
| 15 | +companion object that takes an `rx.Observer` private to the Scala bindings package, thus properly hiding irrelevant |
| 16 | +implementation details from the user-facing API. The `Observer[T]` trait now looks like a clean, native Scala type: |
| 17 | + |
| 18 | +```scala |
| 19 | +trait Observer[-T] { |
| 20 | + def onNext(value: T): Unit |
| 21 | + def onError(error: Throwable): Unit |
| 22 | + def onCompleted(): Unit |
| 23 | +} |
| 24 | + |
| 25 | +object Observer {...} |
| 26 | +``` |
| 27 | + |
| 28 | +To create an instance of a specific `Observer`, say `Observer[SensorEvent]` in user code, you can create a new instance |
| 29 | +of the `Observer` trait by implementing any of the methods that you care about: |
| 30 | +```scala |
| 31 | + val printObserver = new Observer[SensorEvent] { |
| 32 | + override def onNext(value: SensorEvent): Unit = {...value.toString...} |
| 33 | + } |
| 34 | +``` |
| 35 | + or you can use one of the overloads of the companion `Observer` object by passing in implementations of the `onNext`, |
| 36 | + `onError` or `onCompleted` methods. |
| 37 | + |
| 38 | +Note that typically you do not need to create an `Observer` since all of the methods that accept an `Observer[T]` |
| 39 | +(for instance `subscribe`) usually come with overloads that accept the individual methods |
| 40 | +`onNext`, `onError`, and `onCompleted` and will automatically create an `Observer` for you under the covers. |
| 41 | + |
| 42 | +While *technically* it is a breaking change make the `asJavaObserver` property private, you should probably not have |
| 43 | +touched `asJavaObserver` in the first place. If you really feel you need to access the underlying `rx.Observer` |
| 44 | +call `toJava`. |
| 45 | + |
| 46 | +Observable |
| 47 | +---------- |
| 48 | + |
| 49 | +Just like for `Observer`, the `Observable` trait now also hides its `asJavaObservable` property and makes the constructor |
| 50 | +function in the companion object that takes an `rx.Observable` private (but leaves the companion object itself public). |
| 51 | +Again, while *technically* this is a breaking change, this should not have any influence on user code. |
| 52 | + |
| 53 | +```scala |
| 54 | +trait Observable[+T] { |
| 55 | + def subscribe(observer: Observer[T]): Subscription = {...} |
| 56 | + def apply(observer: Observer[T]): Subscription = {...} |
| 57 | + ... |
| 58 | +} |
| 59 | +object Observable { |
| 60 | + def create[T](func: Observer[T] => Subscription): Observable[T] = {...} |
| 61 | + ... |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +The major changes in `Observable` are wrt to the factory methods where too libral use of overloading of the `apply` |
| 66 | +method hindered type inference and made Scala code look unnecessarily different than that in other language bindings. |
| 67 | +All factory methods now have their own name corresponding to the Java and .NET operators |
| 68 | +(plus overloads that take a `Scheduler`). |
| 69 | + |
| 70 | +* `def from[T](future: Future[T]): Observable[T]`, |
| 71 | +* `def from[T](iterable: Iterable[T]): Observable[T]`, |
| 72 | +* `def error[T](exception: Throwable): Observable[T]`, |
| 73 | +* `def empty[T]: Observable[T]`, |
| 74 | +* `def items[T](items: T*): Observable[T], |
| 75 | +* Extension method on `toObservable: Observable[T]` on `List[T]`. |
| 76 | + |
| 77 | +In the *pre-release* of this version, we expose both `apply` and `create` for the mother of all creation functions. |
| 78 | +We would like to solicit feedback which of these two names is preferred |
| 79 | +(or both, but there is a high probability that only one will be chosen). |
| 80 | + |
| 81 | +* `def apply[T](subscribe: Observer[T]=>Subscription): Observable[T]` |
| 82 | +* `def create[T](subscribe: Observer[T] => Subscription): Observable[T]` |
| 83 | + |
| 84 | +Subject |
| 85 | +------- |
| 86 | + |
| 87 | +The `Subject` trait now also hides the underlying Java `asJavaSubject: rx.subjects.Subject[_ >: T, _<: T]` |
| 88 | +and takes only a single *invariant* type parameter `T`. all existing implementations of `Subject` are parametrized |
| 89 | +by a single type, and this reflects that reality. |
| 90 | + |
| 91 | +```scala |
| 92 | +trait Subject[T] extends Observable[T] with Observer[T] {} |
| 93 | +object Subject { |
| 94 | + def apply(): Subject[T] = {...} |
| 95 | +} |
| 96 | +``` |
| 97 | +For each kind of subject, there is a class with a private constructor and a companion object that you should use |
| 98 | +to create a new kind of subject. The subjects that are available are: |
| 99 | + |
| 100 | +* `AsyncSubject[T]()`, |
| 101 | +* `BehaviorSubject[T](value)`, |
| 102 | +* `Subject[T]()`, |
| 103 | +* `ReplaySubject[T]()`. |
| 104 | + |
| 105 | +The latter is still missing various overloads http://msdn.microsoft.com/en-us/library/hh211810(v=vs.103).aspx which |
| 106 | +you can expect to appear once they are added to the underlying RxJava implementation. |
| 107 | + |
| 108 | +Compared with release 0.15.1, the breaking changes in `Subject` for this release are |
| 109 | +making `asJavaSubject` private, and collapsing its type parameters, neither of these should cause trouble, |
| 110 | +and renaming `PublishSubject` to `Subject`. |
| 111 | + |
| 112 | +Schedulers |
| 113 | +---------- |
| 114 | + |
| 115 | +The biggest breaking change compared to the 0.15.1 release is giving `Scheduler` the same structure as the other types. |
| 116 | +The trait itself remains unchanged, except that we made the underlying Java representation hidden as above. |
| 117 | +as part of this reshuffling, the scheduler package has been renamed from `rx.lang.scala.concurrency` |
| 118 | +to `rx.lang.scala.schedulers`. There is a high probability that this package renaming will also happen in RxJava. |
| 119 | + |
| 120 | +```scala |
| 121 | +trait Scheduler {...} |
| 122 | +``` |
| 123 | + |
| 124 | +In the previous release, you created schedulers by selecting them from the `Schedulers` object, |
| 125 | +as in `Schedulers.immediate` or `Schedulers.newThread` where each would return an instance of the `Scheduler` trait. |
| 126 | +However, several of the scheduler implementations have additional methods, such as the `TestScheduler`, |
| 127 | +which already deviated from the pattern. |
| 128 | + |
| 129 | +In this release, we changed this to make scheduler more like `Subject` and provide a family of schedulers |
| 130 | +that you create using their factory function: |
| 131 | + |
| 132 | +* `CurrentThreadScheduler()`, |
| 133 | +* `ExecutorScheduler(executor)`, |
| 134 | +* `ImmediateScheduler()`, |
| 135 | +* `NewThreadScheduler()`, |
| 136 | +* `ScheduledExecutorServiceScheduler(scheduledExecutorService)`, |
| 137 | +* `TestScheduler()`, |
| 138 | +* `ThreadPoolForComputationScheduler()`, |
| 139 | +* `ThreadPoolForIOScheduler()`. |
| 140 | + |
| 141 | +In the future we expect that this list will grow further with new schedulers as they are imported from .NET |
| 142 | +(http://msdn.microsoft.com/en-us/library/system.reactive.concurrency(v=vs.103).aspx). |
| 143 | + |
| 144 | +To make your code compile in the new release you will have to change all occurrences of `Schedulers.xxx` |
| 145 | +into `XxxScheduler()`, and import `rx.lang.scala.schedulers` instead of `rx.lang.scala.schedulers`. |
| 146 | + |
| 147 | +Subscriptions |
| 148 | +------------- |
| 149 | + |
| 150 | +The `Subscription` trait in Scala now has `isUnsubscribed` as a member, effectively collapsing the old `Subscription` |
| 151 | +and `BooleanSubscription`, and the latter has been removed from the public surface. Pending a bug fix in RxJava, |
| 152 | +`SerialSubscription` implements its own `isUnsubscribed`. |
| 153 | + |
| 154 | + |
| 155 | +```scala |
| 156 | +trait Subscription { |
| 157 | + def unsubscribe(): Unit = { ... } |
| 158 | + def isUnsubscribed: Boolean = ... |
| 159 | +} |
| 160 | + |
| 161 | +object Subscription {...} |
| 162 | + ``` |
| 163 | + |
| 164 | + To create a `Subscription` use one of the following factory methods: |
| 165 | + |
| 166 | + * `Subscription{...}`, `Subscription()`, |
| 167 | + * `CompositeSubscription(subscriptions)`, |
| 168 | + * `MultipleAssignmentSubscription()`, |
| 169 | + * `SerialSubscription()`. |
| 170 | + |
| 171 | + In case you do feel tempted to call `new Subscription{...}` directly make sure you wire up `isUnsubscribed` |
| 172 | + and `unsubscribe()` properly, but for all practical purposes you should just use one of the factory methods. |
| 173 | + |
| 174 | +Notifications |
| 175 | +------------- |
| 176 | + |
| 177 | +All underlying wrapped `Java` types in the `Notification` trait are made private like all previous types. The companion |
| 178 | +objects of `Notification` now have both constructor (`apply`) and extractor (`unapply`) functions: |
| 179 | + |
| 180 | +```scala |
| 181 | +object Notification {...} |
| 182 | +trait Notification[+T] { |
| 183 | + override def equals(that: Any): Boolean = {...} |
| 184 | + override def hashCode(): Int = {...} |
| 185 | + def apply[R](onNext: T=>R, onError: Throwable=>R, onCompleted: ()=>R): R = {...} |
| 186 | +} |
| 187 | +``` |
| 188 | +The nested companion objects of `Notification` now have both constructor (`apply`) and extractor (`unapply`) functions: |
| 189 | +```scala |
| 190 | +object Notification { |
| 191 | + object OnNext { def apply(...){}; def unapply(...){...} } |
| 192 | + object OnError { def apply(...){}; def unapply(...){...} } |
| 193 | + object OnCompleted { def apply(...){}; def unapply(...){...} } |
| 194 | +} |
| 195 | +``` |
| 196 | +To construct a `Notification`, you import `rx.lang.scala.Notification._` and use `OnNext("hello")`, |
| 197 | +or `OnError(new Exception("Oops!"))`, or `OnCompleted()`. |
| 198 | + |
| 199 | +To pattern match on a notification you create a partial function like so: `case Notification.OnNext(v) => { ... v ... }`, |
| 200 | +or you use the `apply` function to pass in functions for each possibility. |
| 201 | + |
| 202 | +There are no breaking changes for notifications. |
| 203 | + |
| 204 | +Java Interop Helpers |
| 205 | +-------------------- |
| 206 | + |
| 207 | +Since the Scala traits *wrap* the underlying Java types, yoo may occasionally will have to wrap an unwrap |
| 208 | +between the two representations. The `JavaConversion` object provides helper functions of the form `toJavaXXX` and |
| 209 | +`toScalaXXX` for this purpose, properly hiding how precisely the wrapped types are stored. |
| 210 | +Note the (un)wrap conversions are defined as implicits in Scala, but in the unlikely event that you do need them |
| 211 | +be kind to the reader of your code and call them explicitly. |
| 212 | + |
| 213 | +```scala |
| 214 | +object JavaConversions { |
| 215 | + import language.implicitConversions |
| 216 | + |
| 217 | + implicit def toJavaNotification[T](s: Notification[T]): rx.Notification[_ <: T] = {...} |
| 218 | + implicit def toScalaNotification[T](s: rx.Notification[_ <: T]): Notification[T] = {...} |
| 219 | + implicit def toJavaSubscription(s: Subscription): rx.Subscription = {...} |
| 220 | + implicit def toScalaSubscription(s: rx.Subscription): Subscription = {...} |
| 221 | + implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = {...} |
| 222 | + implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = {...} |
| 223 | + implicit def toJavaObserver[T](s: Observer[T]): rx.Observer[_ >: T] = {...} |
| 224 | + implicit def toScalaObserver[T](s: rx.Observer[_ >: T]): Observer[T] = {...} |
| 225 | + implicit def toJavaObservable[T](s: Observable[T]): rx.Observable[_ <: T] = {...} |
| 226 | + implicit def toScalaObservable[T](observable: rx.Observable[_ <: T]): Observable[T] = {...} |
| 227 | +} |
| 228 | +``` |
0 commit comments