ReactiveX / RxScala

RxScala – Reactive Extensions for Scala – a library for composing asynchronous and event-based programs using observable sequences
Apache License 2.0
888 stars 121 forks source link

RxScala <-> RxJava converters #207

Closed rvanheest closed 7 years ago

rvanheest commented 8 years ago

Hi, based on my question on StackOverflow and the answer/invite by @samuelgruetter I got to work and created a JavaConverters class that is similar to the rx.lang.scala.JavaConversions class but does the conversion between RxJava and RxScala by using the asScala method (and asJava vise versa). The implementation is similar to the one in the Scala SDK (scala.collections.JavaConverters) and offers a way to do a more explicit implicit conversion.

Usage: although the use is quite obvious, I included an example of the intended use below. Given a function that return a RxJava Observable (most likely coming from a Java based API), you transform it to a RxScala Observable using asScala:

import rx.lang.scala.JavaConverters._

object DemoJ2S extends App {

  def getJavaObservable: rx.Observable[Int] = rx.Observable.just(1, 2, 3)

  getJavaObservable.asScala.subscribe(i => println(i))
}

Similarly, you convert a RxScala Observable to an RxJava Observale using asJava:

object DemoS2J extends App {

    def getScalaObservable: Observable[Int] = Observable.just(1, 2, 3)

    getScalaObservable.asJava.subscribe(new Action1[Int] {
        override def call(i: Int): Unit = println(i)
    })
}

Besides Observable I included similar conversions for all types that were already in rx.lang.scala.JavaConversions (Observer, Scheduler, Worker, etc.).

rvanheest commented 8 years ago

in this response the RxJava variants of the types are prefixed with a j

Let me reply to your questions:

Could I add some examples similar to RxScalaDemo? - I actually had some examples but didn't commit them as they were not real tests. Did not see RxScalaDemo before. I added them 55b816b. This was also helpful for me, as I found some type level bugs with it!

spaces instead of tabs - fixed that

Why don't I use asJava for Subscription? - I did, but there are some type level problems with this, because of the relation between Subscription, Subscriber and Observer. For example, if you would have a Subscriber[T] somewhere (say in an Observable.apply), then calling asJava gives a jSubscriber[_ >: T] (welcome to covariance and contravariance!) but downcasting to jObserver[_ >: T] wouldn't work. To fix this the only solution is to give the extension method another name. Same thing (as I discovered in 55b816b) holds for jSubscription! You have to give it other names. As I have experienced that the most use of asJava and asScala is in jObservable to Observable and jObserver to Observer, I figured I use asJava and asScala for the Observer variant and asXSubscriber and asXSubscription for Subscriber and Subscription... I was not able to find any other solution. If you know a better one, feel free to suggest this.

Hope this clears up any of your questions.

samuelgruetter commented 8 years ago

I tried to understand why we can't use asScala/asJava everywhere, so I quickly changed all asScalaXxx to asScala and also for Java, see https://github.com/ReactiveX/RxScala/pull/208. (Aside: Please also have a look at commit https://github.com/ReactiveX/RxScala/pull/208/commits/eb6db125a5aeb97fd909fb7addf2df9aef911465: Always give an explicit return type for public methods, otherwise bad things can happen, see the commit message). If you try to compile it, a scala.reflect.internal.FatalError occurs, you can see it in the Travis CI log. That is, we just found a bug in the Scala compiler :grimacing: Did you get this as well? If you have time, could you please check if this also happens with the latest Scala compiler, and if yes, try to isolate and report it? Finding a scalac bug with RxScala actually happened to me before, and the scalac maintainers could show me a simple work-around, so let's hope they'll know such a work-around for this one as well :wink:

rvanheest commented 8 years ago

Yup, I got a similar error yesterday in the last example as well (I run Scala 2.11.8). Asked about it on https://gitter.im/scala/scala and got a response from @SethTisue (see below).

If I find time in the next couple of days, I'll have a look at this compiler bug. Let's keep in touch on this!

In the meantime I'll fix the Unit types you briefly mentioned...


https://files.gitter.im/scala/scala/6nER/blob

screen shot 2016-09-20 at 06 32 56 screen shot 2016-09-20 at 06 28 15
zsxwing commented 7 years ago

Since scala.collection.JavaConversions could cause a lot of problems implicitly and is deprecated in Scala 2.12.0, I suggest that we also deprecate rx.lang.scala.JavaConversions.

rvanheest commented 7 years ago

@zsxwing I did not know that, but indeed read this in the scaladoc. Maybe this PR could provide a replacement for this. Although I really think that deprecating rx.lang.scala.JavaConversions should be done in a separate PR.

@samuelgruetter I tried to come up with a better solution than using separate keywords for Subscriber and Subscription, but kept walking into this same compiler error. Maybe you could propose a better solution if you know any?

Other than that, I consider this PR as a nice alternative for rx.lang.scala.JavaConversions.

zsxwing commented 7 years ago

Although I really think that deprecating rx.lang.scala.JavaConversions should be done in a separate PR.

Agreed.

samuelgruetter commented 7 years ago

I isolated the scalac bug:

import scala.language.implicitConversions

object Bug extends App {

  class JObserver[T]
  class JSubscriber[T] extends JObserver[T]

  class Foo

  implicit def convertSubscriber[T](s: JSubscriber[_ >: T]): Foo = ???

  implicit def convertObserver[T](s: JObserver[_ >: T]): Foo = ???

  val jSubscriber: JSubscriber[_ >: Int] = ???

  val foo: Foo = jSubscriber // compiler crashes

}

I think it's SI-5559, and it seems that there is no simple work-around...

So I guess we have to live with the more verbose asScalaXxx methods, and I'd say LGTM, ready to merge. Thanks @rvanheest!

rvanheest commented 7 years ago

My pleasure, mate! You guys are doing great stuff and I'm happy to contribute.