Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Scala Bindings #598

Merged
merged 57 commits into from
Dec 10, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8ec1f11
Added release notes for Observer.
AppliedDuality Dec 6, 2013
20da54b
Release notes
AppliedDuality Dec 6, 2013
7737b8d
Added overrides.
AppliedDuality Dec 6, 2013
fdf68a8
Strange changes ...
AppliedDuality Nov 30, 2013
340b3c1
Removing BooleanSubscription
AppliedDuality Dec 6, 2013
b61388f
Making tests work
AppliedDuality Dec 6, 2013
c824c89
Making tests work.
AppliedDuality Dec 6, 2013
98dee16
Modified Subscription tests.
AppliedDuality Dec 6, 2013
0a2cf15
Modified Subscription tests.
AppliedDuality Dec 6, 2013
8128bbe
Modified Subscription tests.
AppliedDuality Dec 6, 2013
4d1506c
Added overloads for Observer companion object
AppliedDuality Dec 6, 2013
8b77db1
Release notes for Notification
AppliedDuality Dec 6, 2013
a565cc0
Cosmetic changes to Notification.
AppliedDuality Dec 6, 2013
a0aa6db
Renamed rx.lang.scala.concurrency to rx.lang.scala.schedulers
AppliedDuality Dec 6, 2013
f0c8cb9
Added test for SerialSubscription
AppliedDuality Dec 6, 2013
3853258
Subjects & Observers
AppliedDuality Dec 7, 2013
1ea162d
private [scala] val asJavaXXX for all.
AppliedDuality Dec 7, 2013
426e252
Tried to improve type inference, alas.
AppliedDuality Dec 7, 2013
405d78d
release notes details
samuelgruetter Dec 7, 2013
31a0770
Re-instantiating commented out tests.
AppliedDuality Dec 7, 2013
ea43e6d
Re-instantiating commented out tests.
AppliedDuality Dec 7, 2013
685e211
Merge pull request #2 from samuelgruetter/RxJavaBugFixesSam2
headinthebox Dec 7, 2013
ab34d6a
Changed def to val in release notes to match source code. Which I co…
AppliedDuality Dec 7, 2013
51c4877
Doc comments for Notification.
AppliedDuality Dec 7, 2013
35d200d
Notification tests.
AppliedDuality Dec 7, 2013
168279e
Notification tests.
AppliedDuality Dec 7, 2013
559b6d8
(Superfluous) Subject tests.
AppliedDuality Dec 7, 2013
d03dd6b
Simplified Subject[-T, +R] extends Observable[R] with Observer[T]
AppliedDuality Dec 7, 2013
de38dbb
Updated release notes for Subject[T]
AppliedDuality Dec 7, 2013
7d858f7
Manually merge branch 'master' of github.com:Netflix/RxJava into Merg…
samuelgruetter Dec 8, 2013
d2c2fbf
README
AppliedDuality Dec 8, 2013
41fb1f3
Interop Helpers
AppliedDuality Dec 8, 2013
80527c9
Documented factory method changes.
AppliedDuality Dec 8, 2013
265a6d2
remove rx.lang.scala.subscriptions.SubscriptionTests,
samuelgruetter Dec 8, 2013
c194d73
fix one constructor usage
samuelgruetter Dec 8, 2013
f7c5dd0
reorg imports of TestSchedulerExample
samuelgruetter Dec 8, 2013
d9f31d1
undo undesired side effects of search/replace
samuelgruetter Dec 8, 2013
44bb01c
fix constructor usage in ObservableTest
samuelgruetter Dec 8, 2013
c335add
Updated release notes
AppliedDuality Dec 8, 2013
f39bca6
renamed `accept` to `apply` for Notification.
AppliedDuality Dec 8, 2013
4ef1a37
CRemoved Assert. prefix from tests
AppliedDuality Dec 8, 2013
9b7aebf
Cleaned up subscriptions to make them idempotent when called from eit…
AppliedDuality Dec 9, 2013
077ac94
Release notes for Notification
AppliedDuality Dec 9, 2013
a620b5c
Layout
AppliedDuality Dec 9, 2013
bbfc649
added apply(Observer) to Observable[T] trait
AppliedDuality Dec 9, 2013
711a18b
rebasing
AppliedDuality Dec 9, 2013
4d3ba16
Added back private PublishSubject[T] object.
AppliedDuality Dec 9, 2013
9effff0
Observer cleanup
AppliedDuality Dec 9, 2013
8c81113
Added default implementation in Subscription trait
AppliedDuality Dec 9, 2013
e9c4476
Experimental extension methods for lists.
AppliedDuality Dec 9, 2013
852f559
Test to show various constructors.
AppliedDuality Dec 9, 2013
2b05109
Made rx.Scheduler bindings private [scala] such that you can access …
AppliedDuality Dec 9, 2013
eec26ae
Empty subscribe()
AppliedDuality Dec 10, 2013
83b0760
Added accept with Observer to match RxJava
AppliedDuality Dec 10, 2013
335ec87
Renamed apply(items: T*) to items.
AppliedDuality Dec 10, 2013
79ef52f
Release notes edits.
AppliedDuality Dec 10, 2013
9af8568
Release notes edits.
AppliedDuality Dec 10, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion language-adaptors/rxjava-scala/Rationale.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ and consumption of Rx values from Java but not for Scala as a producer.
If we take that approach, we can make bindings that feels like a completely native Scala library,
without needing any complications of the Scala side.
```scala
object Observer { …}
object Observable { …}
trait Observable[+T] {
def asJavaObservable: rx.Observable[_ <: T]
}
Expand Down
228 changes: 228 additions & 0 deletions language-adaptors/rxjava-scala/ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
RxScala Release Notes
=====================

This release of the RxScala bindings builds on the previous 0.15 release to make the Rx bindings for Scala
include all Rx types. In particular this release focuses on fleshing out the bindings for the `Subject` and `Scheduler`
types, as well as aligning the constructor functions for `Observable` with those in the RxJava.

Expect to see ongoing additions to make the Scala binding match the equivalent underlying Java API,
as well as minor changes in the existing API as we keep fine-tuning the experience on our way to a V1.0 release.

Observer
--------

In this release we have made the `asJavaObserver` property in `Observable[T]`as well the the factory method in the
companion object that takes an `rx.Observer` private to the Scala bindings package, thus properly hiding irrelevant
implementation details from the user-facing API. The `Observer[T]` trait now looks like a clean, native Scala type:

```scala
trait Observer[-T] {
def onNext(value: T): Unit
def onError(error: Throwable): Unit
def onCompleted(): Unit
}

object Observer {...}
```

To create an instance of a specific `Observer`, say `Observer[SensorEvent]` in user code, you can create a new instance
of the `Observer` trait by implementing any of the methods that you care about:
```scala
val printObserver = new Observer[SensorEvent] {
override def onNext(value: SensorEvent): Unit = {...value.toString...}
}
```
or you can use one of the overloads of the companion `Observer` object by passing in implementations of the `onNext`,
`onError` or `onCompleted` methods.

Note that typically you do not need to create an `Observer` since all of the methods that accept an `Observer[T]`
(for instance `subscribe`) usually come with overloads that accept the individual methods
`onNext`, `onError`, and `onCompleted` and will automatically create an `Observer` for you under the covers.

While *technically* it is a breaking change make the `asJavaObserver` property private, you should probably not have
touched `asJavaObserver` in the first place. If you really feel you need to access the underlying `rx.Observer`
call `toJava`.

Observable
----------

Just like for `Observer`, the `Observable` trait now also hides its `asJavaObservable` property and makes the constructor
function in the companion object that takes an `rx.Observable` private (but leaves the companion object itself public).
Again, while *technically* this is a breaking change, this should not have any influence on user code.

```scala
trait Observable[+T] {
def subscribe(observer: Observer[T]): Subscription = {...}
def apply(observer: Observer[T]): Subscription = {...}
...
}
object Observable {
def create[T](func: Observer[T] => Subscription): Observable[T] = {...}
...
}
```

The major changes in `Observable` are wrt to the factory methods where too libral use of overloading of the `apply`
method hindered type inference and made Scala code look unnecessarily different than that in other language bindings.
All factory methods now have their own name corresponding to the Java and .NET operators
(plus overloads that take a `Scheduler`).

* `def from[T](future: Future[T]): Observable[T]`,
* `def from[T](iterable: Iterable[T]): Observable[T]`,
* `def error[T](exception: Throwable): Observable[T]`,
* `def empty[T]: Observable[T]`,
* `def items[T](items: T*): Observable[T],
* Extension method on `toObservable: Observable[T]` on `List[T]`.

In the *pre-release* of this version, we expose both `apply` and `create` for the mother of all creation functions.
We would like to solicit feedback which of these two names is preferred
(or both, but there is a high probability that only one will be chosen).

* `def apply[T](subscribe: Observer[T]=>Subscription): Observable[T]`
* `def create[T](subscribe: Observer[T] => Subscription): Observable[T]`

Subject
-------

The `Subject` trait now also hides the underlying Java `asJavaSubject: rx.subjects.Subject[_ >: T, _<: T]`
and takes only a single *invariant* type parameter `T`. all existing implementations of `Subject` are parametrized
by a single type, and this reflects that reality.

```scala
trait Subject[T] extends Observable[T] with Observer[T] {}
object Subject {
def apply(): Subject[T] = {...}
}
```
For each kind of subject, there is a class with a private constructor and a companion object that you should use
to create a new kind of subject. The subjects that are available are:

* `AsyncSubject[T]()`,
* `BehaviorSubject[T](value)`,
* `Subject[T]()`,
* `ReplaySubject[T]()`.

The latter is still missing various overloads http://msdn.microsoft.com/en-us/library/hh211810(v=vs.103).aspx which
you can expect to appear once they are added to the underlying RxJava implementation.

Compared with release 0.15.1, the breaking changes in `Subject` for this release are
making `asJavaSubject` private, and collapsing its type parameters, neither of these should cause trouble,
and renaming `PublishSubject` to `Subject`.

Schedulers
----------

The biggest breaking change compared to the 0.15.1 release is giving `Scheduler` the same structure as the other types.
The trait itself remains unchanged, except that we made the underlying Java representation hidden as above.
as part of this reshuffling, the scheduler package has been renamed from `rx.lang.scala.concurrency`
to `rx.lang.scala.schedulers`. There is a high probability that this package renaming will also happen in RxJava.

```scala
trait Scheduler {...}
```

In the previous release, you created schedulers by selecting them from the `Schedulers` object,
as in `Schedulers.immediate` or `Schedulers.newThread` where each would return an instance of the `Scheduler` trait.
However, several of the scheduler implementations have additional methods, such as the `TestScheduler`,
which already deviated from the pattern.

In this release, we changed this to make scheduler more like `Subject` and provide a family of schedulers
that you create using their factory function:

* `CurrentThreadScheduler()`,
* `ExecutorScheduler(executor)`,
* `ImmediateScheduler()`,
* `NewThreadScheduler()`,
* `ScheduledExecutorServiceScheduler(scheduledExecutorService)`,
* `TestScheduler()`,
* `ThreadPoolForComputationScheduler()`,
* `ThreadPoolForIOScheduler()`.

In the future we expect that this list will grow further with new schedulers as they are imported from .NET
(http://msdn.microsoft.com/en-us/library/system.reactive.concurrency(v=vs.103).aspx).

To make your code compile in the new release you will have to change all occurrences of `Schedulers.xxx`
into `XxxScheduler()`, and import `rx.lang.scala.schedulers` instead of `rx.lang.scala.schedulers`.

Subscriptions
-------------

The `Subscription` trait in Scala now has `isUnsubscribed` as a member, effectively collapsing the old `Subscription`
and `BooleanSubscription`, and the latter has been removed from the public surface. Pending a bug fix in RxJava,
`SerialSubscription` implements its own `isUnsubscribed`.


```scala
trait Subscription {
def unsubscribe(): Unit = { ... }
def isUnsubscribed: Boolean = ...
}

object Subscription {...}
```

To create a `Subscription` use one of the following factory methods:

* `Subscription{...}`, `Subscription()`,
* `CompositeSubscription(subscriptions)`,
* `MultipleAssignmentSubscription()`,
* `SerialSubscription()`.

In case you do feel tempted to call `new Subscription{...}` directly make sure you wire up `isUnsubscribed`
and `unsubscribe()` properly, but for all practical purposes you should just use one of the factory methods.

Notifications
-------------

All underlying wrapped `Java` types in the `Notification` trait are made private like all previous types. The companion
objects of `Notification` now have both constructor (`apply`) and extractor (`unapply`) functions:

```scala
object Notification {...}
trait Notification[+T] {
override def equals(that: Any): Boolean = {...}
override def hashCode(): Int = {...}
def apply[R](onNext: T=>R, onError: Throwable=>R, onCompleted: ()=>R): R = {...}
}
```
The nested companion objects of `Notification` now have both constructor (`apply`) and extractor (`unapply`) functions:
```scala
object Notification {
object OnNext { def apply(...){}; def unapply(...){...} }
object OnError { def apply(...){}; def unapply(...){...} }
object OnCompleted { def apply(...){}; def unapply(...){...} }
}
```
To construct a `Notification`, you import `rx.lang.scala.Notification._` and use `OnNext("hello")`,
or `OnError(new Exception("Oops!"))`, or `OnCompleted()`.

To pattern match on a notification you create a partial function like so: `case Notification.OnNext(v) => { ... v ... }`,
or you use the `apply` function to pass in functions for each possibility.

There are no breaking changes for notifications.

Java Interop Helpers
--------------------

Since the Scala traits *wrap* the underlying Java types, yoo may occasionally will have to wrap an unwrap
between the two representations. The `JavaConversion` object provides helper functions of the form `toJavaXXX` and
`toScalaXXX` for this purpose, properly hiding how precisely the wrapped types are stored.
Note the (un)wrap conversions are defined as implicits in Scala, but in the unlikely event that you do need them
be kind to the reader of your code and call them explicitly.

```scala
object JavaConversions {
import language.implicitConversions

implicit def toJavaNotification[T](s: Notification[T]): rx.Notification[_ <: T] = {...}
implicit def toScalaNotification[T](s: rx.Notification[_ <: T]): Notification[T] = {...}
implicit def toJavaSubscription(s: Subscription): rx.Subscription = {...}
implicit def toScalaSubscription(s: rx.Subscription): Subscription = {...}
implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = {...}
implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = {...}
implicit def toJavaObserver[T](s: Observer[T]): rx.Observer[_ >: T] = {...}
implicit def toScalaObserver[T](s: rx.Observer[_ >: T]): Observer[T] = {...}
implicit def toJavaObservable[T](s: Observable[T]): rx.Observable[_ <: T] = {...}
implicit def toScalaObservable[T](observable: rx.Observable[_ <: T]): Observable[T] = {...}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import scala.concurrent.duration._
object Olympics {
case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String)

def mountainBikeMedals: Observable[Medal] = Observable(
Observable(
def mountainBikeMedals: Observable[Medal] = Observable.items(
Observable.items(
Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"),
Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"),
Medal(1996, "Atlanta 1996", "cross-country men", "Silver", "Thomas FRISCHKNECHT", "Switzerland"),
Expand All @@ -31,7 +31,7 @@ object Olympics {
Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America")
),
fourYearsEmpty,
Observable(
Observable.items(
Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"),
Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"),
Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"),
Expand All @@ -40,7 +40,7 @@ object Olympics {
Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland")
),
fourYearsEmpty,
Observable(
Observable.items(
Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"),
Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"),
Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"),
Expand All @@ -49,7 +49,7 @@ object Olympics {
Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany")
),
fourYearsEmpty,
Observable(
Observable.items(
Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"),
Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"),
Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"),
Expand All @@ -58,7 +58,7 @@ object Olympics {
Medal(2008, "Beijing 2008", "cross-country men", "Bronze", "Nino SCHURTER", "Switzerland")
),
fourYearsEmpty,
Observable(
Observable.items(
Medal(2012, "London 2012", "cross-country men", "Gold", "Jaroslav KULHAVY", "Czech Republic"),
Medal(2012, "London 2012", "cross-country men", "Silver", "Nino SCHURTER", "Switzerland"),
Medal(2012, "London 2012", "cross-country men", "Bronze", "Marco Aurelio FONTANA", "Italy"),
Expand All @@ -80,7 +80,7 @@ object Olympics {
// So we don't use this:
// Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false)
// But we just return empty, which completes immediately
Observable()
Observable.empty[Medal]
}

}
Loading