https://github.com/reactivex/rxclojure
RxJava bindings for Clojure
https://github.com/reactivex/rxclojure
Last synced: 3 months ago
JSON representation
RxJava bindings for Clojure
- Host: GitHub
- URL: https://github.com/reactivex/rxclojure
- Owner: ReactiveX
- License: apache-2.0
- Created: 2014-08-19T03:45:43.000Z (over 11 years ago)
- Default Branch: 0.x
- Last Pushed: 2020-02-22T07:03:09.000Z (almost 6 years ago)
- Last Synced: 2025-09-09T20:53:45.353Z (4 months ago)
- Language: Clojure
- Homepage:
- Size: 9.07 MB
- Stars: 362
- Watchers: 20
- Forks: 20
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
Clojure bindings for RxJava.
# Binaries
Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-clojure%22).
Example for Leiningen:
```clojure
[io.reactivex/rxclojure "x.y.z"]
```
and for Gradle:
```groovy
compile 'io.reactivex:rxclojure:x.y.z'
```
and for Maven:
```xml
io.reactivex
rxclojure
x.y.z
```
and for Ivy:
```xml
```
# Clojure Bindings
This library provides convenient, idiomatic Clojure bindings for RxJava.
The bindings try to present an API that will be comfortable and familiar to a Clojure programmer that's familiar with the sequence operations in `clojure.core`. It "fixes" several issues with using RxJava with raw Java interop, for example:
* Argument lists are in the "right" order. So in RxJava, the function applied in `Observable.map` is the second argument, while here it's the first argument with one or more Observables as trailing arguments
* Operators take normal Clojure functions as arguments, bypassing need for the interop described below
* Predicates accomodate Clojure's notion of truth
* Operators are generally names as they would be in `clojure.core` rather than the Rx names
There is no object wrapping going on. That is, all functions return normal `rx.Observable` objects, so you can always drop back to Java interop for anything that's missing in this wrapper.
## Basic Usage
Most functionality resides in the `rx.lang.clojure.core` namespace and for the most part looks like normal Clojure sequence manipulation:
```clojure
(require '[rx.lang.clojure.core :as rx])
(->> my-observable
(rx/map (comp clojure.string/lower-case :first-name))
(rx/map clojure.string/lower-case)
(rx/filter #{"bob"})
(rx/distinct)
(rx/into []))
;=> An Observable that emits a single vector of names
```
Blocking operators, which are useful for testing, but should otherwise be avoided, reside in `rx.lang.clojure.blocking`. For example:
```clojure
(require '[rx.lang.clojure.blocking :as rxb])
(rxb/doseq [{:keys [first-name]} users-observable]
(println "Hey," first-name))
;=> nil
```
## Open Issues
* The missing stuff mentioned below
* `group-by` val-fn variant isn't implemented in RxJava
* There are some functions for defining customer Observables and Operators (`subscriber`, `operator*`, `observable*`). I don't think these are really enough for serious operator implementation, but I'm hesitant to guess at an abstraction at this point. These will probably change dramatically.
## What's Missing
This library is an ongoing work in progress driven primarily by the needs of one team at Netflix. As such some things are currently missing:
* Highly-specific operators that we felt cluttered the API and were easily composed from existing operators, especially since we're in not-Java land. For example, `Observable.sumLong()`.
* Most everything involving schedulers
* Most everything involving time
* `Observable.window` and `Observable.buffer`. Who knows which parts of these beasts to wrap?
Of course, contributions that cover these cases are welcome.
# Low-level Interop
This adaptor provides functions and macros to ease Clojure/RxJava interop. In particular, there are functions and macros for turning Clojure functions and code into RxJava `Func*` and `Action*` interfaces without the tedium of manually reifying the interfaces.
## Basic Usage
### Requiring the interop namespace
The first thing to do is to require the namespace:
```clojure
(ns my.namespace
(:require [rx.lang.clojure.interop :as rx])
(:import [rx Observable]))
```
or, at the REPL:
```clojure
(require '[rx.lang.clojure.interop :as rx])
```
### Using rx/fn
Once the namespace is required, you can use the `rx/fn` macro anywhere RxJava wants a `rx.functions.Func` object. The syntax is exactly the same as `clojure.core/fn`:
```clojure
(-> my-observable
(.map (rx/fn [v] (* 2 v))))
```
If you already have a plain old Clojure function you'd like to use, you can pass it to the `rx/fn*` function to get a new object that implements `rx.functions.Func`:
```clojure
(-> my-numbers
(.reduce (rx/fn* +)))
```
### Using rx/action
The `rx/action` macro is identical to `rx/fn` except that the object returned implements `rx.functions.Action` interfaces. It's used in `subscribe` and other side-effect-y contexts:
```clojure
(-> my-observable
(.map (rx/fn* transform-data))
(.finallyDo (rx/action [] (println "Finished transform")))
(.subscribe (rx/action [v] (println "Got value" v))
(rx/action [e] (println "Get error" e))
(rx/action [] (println "Sequence complete"))))
```
### Using Observable/create
As of 0.17, `rx.Observable/create` takes an implementation of `rx.Observable$OnSubscribe` which is basically an alias for `rx.functions.Action1` that takes an `rx.Subscriber` as its argument. Thus, you can just use `rx/action` when creating new observables:
```clojure
; A simple observable that emits 0..9 taking unsubscribe into account
(Observable/create (rx/action [^rx.Subscriber s]
(loop [i 0]
(when (and (< i 10) (.isUnsubscribed s))
(.onNext s i)
(recur (inc i))))
(.onCompleted s)))
```
## Gotchas
Here are a few things to keep in mind when using this interop:
* Keep in mind the (mostly empty) distinction between `Func` and `Action` and which is used in which contexts
* If there are multiple Java methods overloaded by `Func` arity, you'll need to use a type hint to let the compiler know which one to choose.
* Methods that take a predicate (like filter) expect the predicate to return a boolean value. A function that returns a non-boolean value will result in a `ClassCastException`.