3

It is common practice in JavaScript to have a function that takes in an options parameter, like so:

function foo({ bar1, bar2, bar3 }) {}
foo({ bar1: 5, bar2: 3 })

In Reason/OCaml, one would prefer to use labelled arguments for these functions:

let foo = (~bar1, ~bar2, ~bar) => {}
foo(~bar1=5, ~bar2=3, ())

Now, I know there is this way of creating Reason/Bucklescript bindings for functions like these:

type barObj;
[@bs.obj] external makeBarObj : (
  ~bar1: int=?,
  ~bar2: int=?,
  ~bar3: int=?,
  unit
) => barObj = "";

external foo : barObj => t = "foo";

foo(makeBarObj(~bar1=5, ~bar2=3, ());

Is there, however, a simpler way of writing bindings for such functions? My problem with this approach is that it gets quite "long" when calling a function that takes in an options object, especially if it is a polymorphic argument, e.g.:

foo(`BarObj(makebarObj(~bar1=5, ~bar2=3, ())));
David Gomes
  • 5,644
  • 16
  • 60
  • 103

2 Answers2

3

You can construct the object directly instead of using a separate function:

[@bs.val] external foo : Js.t({..}) => t = "";
let foo = (~bar1=?, ~bar2=?, ~bar3=?, unit) =>
  foo({
    "bar1": Js.Nullable.from_opt(bar1),
    "bar2": Js.Nullable.from_opt(bar2),
    "bar3": Js.Nullable.from_opt(bar3)
  });

Which can be called with just

foo(~bar1=5, ~bar2=3, ());

But beware that this isn't exactly equivalent to what [@bs.obj] produces since properties being undefined are not always interpreted as not being defined.

If you need to pass it wrapped in a variant, you could have the object construction function wrap it. You can also usually define a set of functions instead:

fooWithString("bar");
fooWithOptions(~bar1=5, ~bar2=3, ());
glennsl
  • 28,186
  • 12
  • 57
  • 75
  • `If you need to pass it wrapped in a variant, you could have the object construction function wrap it.` Can you clarify how one would do that exactly? – David Gomes Jan 13 '18 at 18:25
  • 1
    If you construct the object manually, you can just wrap it before you return it: ``let makeBarObj = (~bar1=?, ...) => `BarObj({ "bar1": Js.Nullable.from_opt(bar1), ...});`` – glennsl Jan 13 '18 at 19:20
1

Another hypothesis seems to be this one:

[@bs.obj]
external makeBarObj : (~bar1: string=?, ~bar2: int=?, ~bar3: string=?, unit) => barObj =
  "";

[@bs.val] external foo : barObj => t = "foo";

let foo = (~bar1=?, ~bar2=?, ~bar3=?) => foo(makeBarObj(~bar1?, ~bar2?, ~bar3?, ()));

And then this way, the client of the API can simply call:

foo(~bar1=5, ~bar2=3, ())

It's basically the same thing as the solution presented in the question, but this hides the object conversion code in the library so that the client doesn't need to worry about it.

David Gomes
  • 5,644
  • 16
  • 60
  • 103