Version 0.10.0 - Static Language Support by benjchristensen · Pull Request #304 · ReactiveX/RxJava (original) (raw)
Manual merge of pull #300 from @mattrjacobs
This will make RxJava completely static by removing all Object overloads (see #208 and #204).
I'm submitting this before it being 100% ready so people can review and provide feedback.
Open items to append to this pull request before merging:
1) subscribe with map is not handled yet
The following signature needs to be made static. Right now the lack of this combined with removal of Functions.from dynamic language functionality has broken this.
public Subscription subscribe(final Map<String, Object> callbacks)
2) Core artifact naming convention
Should rxjava-core-x.y.x.jar become rxjava-x.y.z.jar since the concept of core+language no longer applies?
I think I'd prefer this:
- rxjava-x.y.z.jar
- rxjava-groovy-x.y.z.jar
- rxjava-clojure-x.y.z.jar
- rxjava-scala-x.y.z.jar
- rxjava-jruby-x.y.z.jar
- rxjava-kotlin-x.y.z.jar
- rxjava-dynamic-x.y.z.jar (object overload for any language)
- rxjava-groovy-clojure-x.y.z.jar (multi-language jar)
Only one of those jars is needed hence the reason why I think the 'core' term is no longer needed as it communicated the fact it was always needed.
Any contrib modules would be: rxjava-contrib-module-name-x.y.x.jar
3) Dependencies from languages to core still exist
The build still will result in Maven Central POM files requires rxjava-core from the language version despite that not being the case. Need to eliminate this dependency in the artifact.
Implementation notes originally posted at #204 (comment):
After implementing and throwing away a few different approaches we have landed on a solution we feel will balance the various competing priorities.
Our goals are:
- support static typing for Java/Scala/Kotlin etc by removing the Object overloads
- support any JVM language, static or dynamically typed
- allow all languages to use the same
rx.Observable
class so that we don't divide libraries with helpers such asGroovyObservable
,ClojureObservable
etc that then need to be converted back and forth when doing interop - do not require special classloaders or agents to enable runtime bytecode generation
- do not remove static operators to enable proxying
- small jars and limited or no dependencies
The solution we have arrived at will work as follows:
- The rxjava-core source code will delete all Object overload methods and be pure static java.
- Any language that supports functional interfaces directly (such as Java 8 and XTend) can use the Java core version directly.
- Languages needing specific lambda/clojure type mapping to the Func_/Action_ types will have language specific Jars created via build-time bytecode generation.
- Any method with a Func_/Action_ argument will be overloaded with a version supporting the language requirements.
For example:
The default Java version:
public static Observable filter(Observable that, Func1<T, Boolean> predicate)
A Groovy version:
public static Observable filter(Observable that, groovy.lang.Closure predicate)
- A jar per language will be created as follows:
- rxjava-x.y.z.jar
- rxjava-groovy-x.y.z.jar
- rxjava-clojure-x.y.z.jar
- rxjava-scala-x.y.z.jar
- rxjava-jruby-x.y.z.jar
- rxjava-kotlin-x.y.z.jar
A project will include just the jar that meets their language needs, there will no longer be a "core" jar plus the language adaptor.
The drawback of this is that mixing two of these in a classpath will result in non-deterministic loading (whichever is loaded last wins) and that is the version that will be used. This means if a library depends on rxjava.jar but is using Groovy and needs rxjava-groovy.jar it is up to the developer of that project to make sure they have only the rxjava-groovy.jar version. This is not ideal but is a one-time pain setting up a build and is better than the constant pain of missing static typing or converting to/from different Observable implementations for different languages.
- At this time we are optimizing for projects using a single language or Java + another language. If there are use cases where people are trying to mix multiple languages in a very polyglot manner we have two options:
- include an rxjava-dynamic.jar version that re-adds the Object overloads
- include build configs for common combinations of languages such as rxjava-groovy-clojure.jar
- Language adaptations (such as clojure which has preferred idioms that necessitate wrapping) will still be possible through the language-adaptor projects and be included in the appropriate language jars.
This should not break any code but will require a slight change to the build dependencies in your project when we release this.
We hope that this enables the RxJava project to better achieve its goal of being polyglot and targeting the JVM in general and not any specific languages without sacrificing interop or idiomatic usage in each of the languages.