Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bindings for functions with options parameter

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, ())));
like image 398
David Gomes Avatar asked Feb 26 '26 20:02

David Gomes


2 Answers

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, ());
like image 86
glennsl Avatar answered Mar 01 '26 04:03

glennsl


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.

like image 22
David Gomes Avatar answered Mar 01 '26 04:03

David Gomes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!