Search code examples
ffireasonbucklescript

ReasonML binding function with config having fixed string values


Lets say, that I have this function in Javascript which can generate string based on proper configuration:

function func(config) {
  // ...
}

also, let's assume, that the config variable has structure as below (all of these can be not given to function call):

{
  "color": string,  // can be: "blue", "red", "green"
  "number": int,    // can be: any number
  "other": string,  // can be: "x", "y"
}

How to create proper binding for this? I'm stuck with:

[@bs.deriving abstract]
type options = {
  [@bs.optional]
  color: [@bs.string] [ | `blue | `red | `green ]
  [@bs.optional]
  number: int,
  [@bs.optional]
  other: [@bs.string] [ | `x | `y ]
}

[@bs.module]
external func: options => string = "func";

But it does not work when trying to use like this:

let config = MyModule.config(
  ~color=`blue,
  ~number=123,
  ~other=`x
);

let value = MyModule.func(config);

The color and other values are integers, not strings.


Solution

  • This is a case of a JavaScript idiom for named parameters (objects with optional fields), needing to be adapted to the OCaml/ReasonML idiom (functions with actual labelled parameters). You would do this in three steps. Step 1, as Glenn showed, define an external for the config:

    type config;
    [@bs.obj] external config: (
      ~color:[@bs.string] [`blue | `red | `green]=?,
      ~number:int=?,
      ~other:[@bs.string] [`x | `y]=?,
     unit,
    ) => config = "";
    

    Step 2, bind to the JavaScript function using the JavaScript style of the config object:

    [@bs.val] external func: config => string = "";
    

    Step 3, wrap the JavaScript function binding in an OCaml-idiomatic function with labelled parameters:

    let func(~color=?, ~number=?, ~other=?, ()) = ()
      |> config(~color?, ~number?, ~other?)
      |> func;
    

    You can use it like this:

    let result = func(~color=`blue, ());