How to bind to an existing JS function that returns multiple values in ReasonML?


#1

I am trying to write a binding for Rx.js - merge operator. Basically, it is a variadic function that takes multiple observables as input and produces a new observable which is an untagged union of all the values emitted by input observables.

This is my current solution:

type 'a observable
type untagged
type anyObs = untagged observable

external merge2 : 'a observable -> 'b observable -> anyObs = "merge" [@@bs.module "rxjs"]
external merge3 : 'a observable -> 'b observable -> 'c observable -> anyObs = "merge" [@@bs.module "rxjs"]

external merge4 :
  'a observable -> 'b observable -> 'c observable -> 'd observable -> anyObs = "merge"
  [@@bs.module "rxjs"]

But this solution is not very elegant. The caller has to use something like Js.Types.classify later when projecting output observable.

Is this the only solution? If not, what is the formal way of doing this?


#2

This RxJS merge function is not very OCaml-ish. In the OCaml/Reason world we prefer to use tagged union types. I would actually prefer to bind the function as something like:

external merge : 'a observable array -> 'a observable = "" [@@bs.module "rxjs"] [@@bs.variadic]

Now it would be up to the user to pass in observables of the same type, which thanks to tagged union types like variants and polymorphic variants, is fairly easy. If you have a timer observable that outputs ints, you can tag each int using the map function:

let times = 1_000 |> interval |> map (fun [@bs] time -> `time time)

Similarly you can tag clicks:

let clicks = map (fun [@bs] click -> `click click) (fromEvent document "click")

Now you can pass in these tagged observables in to merge:

let timesAndClicks = merge [|times; clicks|]

And when consuming the merged observable you can pattern-match on the values using the ‘time’ or ‘click’ tags.


#3

Hmm. That looks more idiomatic way of doing things. Thanks!!!