ReasonML binding function with config having fixed string valuesHow to bind a whole module as a function?How to define a binding that accepts multiple types in the function signature using reason-react?How to defined component /binding when using React ref in Reasonml?How can I parse a string to an integer with Reasonml/Bucklescript?Bindings for functions with options parameterDoes ReasonML have an equivalent of F#'s Computation Expressions?Deploying a ReasonML function to Google Cloud FunctionsHow to write reasonml binding for a union typeHow do I include untyped-JavaScript, from an adjacent file, in ReasonML bindings?How to write a function in ReasonML with a type variable to accept any type of parameter?

Payment instructions from HomeAway look fishy to me

What's up with this leaf?

How do photons get into the eyes?

What are the peak hours for public transportation in Paris?

My coworkers think I had a long honeymoon. Actually I was diagnosed with cancer. How do I talk about it?

Question about JavaScript Math.random() and basic logic

Can a user sell my software (MIT license) without modification?

From the list of 3-tuples, how can I select tuples which contain one for more nines?

When writing an error prompt, should we end the sentence with a exclamation mark or a dot?

How many pairs of subsets can be formed?

How to translate “Me doing X” like in online posts?

Why does Kathryn say this in 12 Monkeys?

Phone number to a lounge, or lounges generally

Orange material in grout lines - need help to identify

Does an ice chest packed full of frozen food need ice?

Implement Homestuck's Catenative Doomsday Dice Cascader

Should an arbiter claim draw at a K+R vs K+R endgame?

Is it possible to (7 day) schedule sleep time of a hard drive?

Average spam confidence

What does the "c." listed under weapon length mean?

Why is the relationship between frequency and pitch exponential?

Etymology of 'calcit(r)are'?

Did the first version of Linux developed by Linus Torvalds have a GUI?

Are "living" organ banks practical?



ReasonML binding function with config having fixed string values


How to bind a whole module as a function?How to define a binding that accepts multiple types in the function signature using reason-react?How to defined component /binding when using React ref in Reasonml?How can I parse a string to an integer with Reasonml/Bucklescript?Bindings for functions with options parameterDoes ReasonML have an equivalent of F#'s Computation Expressions?Deploying a ReasonML function to Google Cloud FunctionsHow to write reasonml binding for a union typeHow do I include untyped-JavaScript, from an adjacent file, in ReasonML bindings?How to write a function in ReasonML with a type variable to accept any type of parameter?






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








2















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] [

[@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.










share|improve this question
























  • There's several bugs in your example code: 1. There's a missing comma at the end of the color field definition, and 2. The call to MyModule.config, which technically also should be MyModule.options, is partially applied because it's missing the final unit argument.

    – glennsl
    Mar 25 at 16:02

















2















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] [

[@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.










share|improve this question
























  • There's several bugs in your example code: 1. There's a missing comma at the end of the color field definition, and 2. The call to MyModule.config, which technically also should be MyModule.options, is partially applied because it's missing the final unit argument.

    – glennsl
    Mar 25 at 16:02













2












2








2








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] [

[@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.










share|improve this question
















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] [

[@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.







ffi reason bucklescript






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 25 at 16:00









glennsl

13.7k103352




13.7k103352










asked Mar 24 at 15:31









Krzysztof TrzosKrzysztof Trzos

3,840124577




3,840124577












  • There's several bugs in your example code: 1. There's a missing comma at the end of the color field definition, and 2. The call to MyModule.config, which technically also should be MyModule.options, is partially applied because it's missing the final unit argument.

    – glennsl
    Mar 25 at 16:02

















  • There's several bugs in your example code: 1. There's a missing comma at the end of the color field definition, and 2. The call to MyModule.config, which technically also should be MyModule.options, is partially applied because it's missing the final unit argument.

    – glennsl
    Mar 25 at 16:02
















There's several bugs in your example code: 1. There's a missing comma at the end of the color field definition, and 2. The call to MyModule.config, which technically also should be MyModule.options, is partially applied because it's missing the final unit argument.

– glennsl
Mar 25 at 16:02





There's several bugs in your example code: 1. There's a missing comma at the end of the color field definition, and 2. The call to MyModule.config, which technically also should be MyModule.options, is partially applied because it's missing the final unit argument.

– glennsl
Mar 25 at 16:02












3 Answers
3






active

oldest

votes


















1














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, ());





share|improve this answer
































    1














    The @bs attributes are often poorly thought out hacks that you shouldn't expect to work well with other attributes, or really with anything other than exactly what the documentation explains or shows examples of. However, if an attribute is used where it is not intended you'll usually at least get a warning about the attribute being unused, which your code does.



    @bs.string in particular only works on types at the outermost level of externals, i.e. on types whose values will be passed directly to the external function. There is also a way to create JavaScript objects using external functions which also happens to use less magic and give you much more control over the API. As far as I'm aware, the only downside compared to @bs.deriving is that you can't override field names using something like @bs.as. They have to be valid OCaml identifiers.



    Here's your example implemented using an external function annotated with @bs.obj:



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


    To use it you call it exactly as with @bs.deriving:



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


    But even with this I've encountered edge cases where integer values are passed in instead of strings. For this reason I tend to avoid the polymorphic variant attributes altogether and instead use ordinary variants along with conversion functions. This has the added benefit of being more idiomatic, blending in better and being more interoperable with non-BuckleScript code.



    Here's what your example might look like using this approach:



    type color = Blue | Red | Green;
    let colorToString = fun
    | Blue => "blue"
    | Red => "red"
    | Green => "green";

    type other = X | Y;
    let otherToString = fun
    | X => "x"
    | Y => "y";

    [@bs.obj] external options : (
    ~color:string=?,
    ~number:int=?,
    ~other:string=?,
    unit
    ) => options = "";

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

    let func = (~color=?, ~number=?, ~other=?, ()) =>
    func(options(
    ~color = ?Belt.Option.map(color, colorToString),
    ~number?,
    ~other = ?Belt.Option.map(other, otherToString),
    ()));

    let config = func(~color=Blue,~number=123, ~other=X, ());





    share|improve this answer

























    • It indeed looks like polymorphic variants don't need to be used in those types of examples. Thank you for your response! I'm implementing new binding for randomColor library and used your version, for now: LINK

      – Krzysztof Trzos
      Mar 26 at 16:34











    • @KrzysztofTrzos one thing to be aware of with this approach is that BuckleScript may not be able to optimize out all the conversion functions out. With the polymorphic variants approach, you 'pay for for you use'.

      – Yawar
      Mar 26 at 16:58











    • @Yawar So what is the cost of this solution and what issues can it bring?

      – Krzysztof Trzos
      Mar 26 at 17:36











    • @Yawar I would go even further and say it very likely won't, unless you're doing everything in a single module. But it's at least sound. That's why I use this approach for bs-webapi and bs-fetch, which are intended as low-level bindings and otherwise a good fit for @bs.string had it worked properly. There are other ways of reducing the overhead, however, such as using constants instead of variants like I do in bs-fetch#next, but then you sacrifice exhaustiveness checking... Unfortunately there is no simple answer here.

      – glennsl
      Mar 26 at 17:45











    • Also, if bsc had been a little bit smarter it could have compiled the conversion functions into some pretty compact lookup tables.

      – glennsl
      Mar 26 at 18:02


















    0














    This is because in reality, those values are variants, instead of trying to make it exactly like JavaScript, I would rather try something more idiomatic to Reason:



    type color = Blue | Green | Red;
    type coords = X | Y;
    type config =
    color,
    coords,
    number: int
    ;

    let func = (config: config) => "something"


    And then inside your function actually return strings (if that is what you really need) by pattern matching on the correct values provided to config.



    See the working code here.



    Hope it helps!






    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "1"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













      draft saved

      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55325408%2freasonml-binding-function-with-config-having-fixed-string-values%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      1














      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, ());





      share|improve this answer





























        1














        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, ());





        share|improve this answer



























          1












          1








          1







          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, ());





          share|improve this answer















          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, ());






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Mar 27 at 18:46

























          answered Mar 25 at 18:00









          YawarYawar

          7,24933758




          7,24933758























              1














              The @bs attributes are often poorly thought out hacks that you shouldn't expect to work well with other attributes, or really with anything other than exactly what the documentation explains or shows examples of. However, if an attribute is used where it is not intended you'll usually at least get a warning about the attribute being unused, which your code does.



              @bs.string in particular only works on types at the outermost level of externals, i.e. on types whose values will be passed directly to the external function. There is also a way to create JavaScript objects using external functions which also happens to use less magic and give you much more control over the API. As far as I'm aware, the only downside compared to @bs.deriving is that you can't override field names using something like @bs.as. They have to be valid OCaml identifiers.



              Here's your example implemented using an external function annotated with @bs.obj:



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


              To use it you call it exactly as with @bs.deriving:



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


              But even with this I've encountered edge cases where integer values are passed in instead of strings. For this reason I tend to avoid the polymorphic variant attributes altogether and instead use ordinary variants along with conversion functions. This has the added benefit of being more idiomatic, blending in better and being more interoperable with non-BuckleScript code.



              Here's what your example might look like using this approach:



              type color = Blue | Red | Green;
              let colorToString = fun
              | Blue => "blue"
              | Red => "red"
              | Green => "green";

              type other = X | Y;
              let otherToString = fun
              | X => "x"
              | Y => "y";

              [@bs.obj] external options : (
              ~color:string=?,
              ~number:int=?,
              ~other:string=?,
              unit
              ) => options = "";

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

              let func = (~color=?, ~number=?, ~other=?, ()) =>
              func(options(
              ~color = ?Belt.Option.map(color, colorToString),
              ~number?,
              ~other = ?Belt.Option.map(other, otherToString),
              ()));

              let config = func(~color=Blue,~number=123, ~other=X, ());





              share|improve this answer

























              • It indeed looks like polymorphic variants don't need to be used in those types of examples. Thank you for your response! I'm implementing new binding for randomColor library and used your version, for now: LINK

                – Krzysztof Trzos
                Mar 26 at 16:34











              • @KrzysztofTrzos one thing to be aware of with this approach is that BuckleScript may not be able to optimize out all the conversion functions out. With the polymorphic variants approach, you 'pay for for you use'.

                – Yawar
                Mar 26 at 16:58











              • @Yawar So what is the cost of this solution and what issues can it bring?

                – Krzysztof Trzos
                Mar 26 at 17:36











              • @Yawar I would go even further and say it very likely won't, unless you're doing everything in a single module. But it's at least sound. That's why I use this approach for bs-webapi and bs-fetch, which are intended as low-level bindings and otherwise a good fit for @bs.string had it worked properly. There are other ways of reducing the overhead, however, such as using constants instead of variants like I do in bs-fetch#next, but then you sacrifice exhaustiveness checking... Unfortunately there is no simple answer here.

                – glennsl
                Mar 26 at 17:45











              • Also, if bsc had been a little bit smarter it could have compiled the conversion functions into some pretty compact lookup tables.

                – glennsl
                Mar 26 at 18:02















              1














              The @bs attributes are often poorly thought out hacks that you shouldn't expect to work well with other attributes, or really with anything other than exactly what the documentation explains or shows examples of. However, if an attribute is used where it is not intended you'll usually at least get a warning about the attribute being unused, which your code does.



              @bs.string in particular only works on types at the outermost level of externals, i.e. on types whose values will be passed directly to the external function. There is also a way to create JavaScript objects using external functions which also happens to use less magic and give you much more control over the API. As far as I'm aware, the only downside compared to @bs.deriving is that you can't override field names using something like @bs.as. They have to be valid OCaml identifiers.



              Here's your example implemented using an external function annotated with @bs.obj:



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


              To use it you call it exactly as with @bs.deriving:



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


              But even with this I've encountered edge cases where integer values are passed in instead of strings. For this reason I tend to avoid the polymorphic variant attributes altogether and instead use ordinary variants along with conversion functions. This has the added benefit of being more idiomatic, blending in better and being more interoperable with non-BuckleScript code.



              Here's what your example might look like using this approach:



              type color = Blue | Red | Green;
              let colorToString = fun
              | Blue => "blue"
              | Red => "red"
              | Green => "green";

              type other = X | Y;
              let otherToString = fun
              | X => "x"
              | Y => "y";

              [@bs.obj] external options : (
              ~color:string=?,
              ~number:int=?,
              ~other:string=?,
              unit
              ) => options = "";

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

              let func = (~color=?, ~number=?, ~other=?, ()) =>
              func(options(
              ~color = ?Belt.Option.map(color, colorToString),
              ~number?,
              ~other = ?Belt.Option.map(other, otherToString),
              ()));

              let config = func(~color=Blue,~number=123, ~other=X, ());





              share|improve this answer

























              • It indeed looks like polymorphic variants don't need to be used in those types of examples. Thank you for your response! I'm implementing new binding for randomColor library and used your version, for now: LINK

                – Krzysztof Trzos
                Mar 26 at 16:34











              • @KrzysztofTrzos one thing to be aware of with this approach is that BuckleScript may not be able to optimize out all the conversion functions out. With the polymorphic variants approach, you 'pay for for you use'.

                – Yawar
                Mar 26 at 16:58











              • @Yawar So what is the cost of this solution and what issues can it bring?

                – Krzysztof Trzos
                Mar 26 at 17:36











              • @Yawar I would go even further and say it very likely won't, unless you're doing everything in a single module. But it's at least sound. That's why I use this approach for bs-webapi and bs-fetch, which are intended as low-level bindings and otherwise a good fit for @bs.string had it worked properly. There are other ways of reducing the overhead, however, such as using constants instead of variants like I do in bs-fetch#next, but then you sacrifice exhaustiveness checking... Unfortunately there is no simple answer here.

                – glennsl
                Mar 26 at 17:45











              • Also, if bsc had been a little bit smarter it could have compiled the conversion functions into some pretty compact lookup tables.

                – glennsl
                Mar 26 at 18:02













              1












              1








              1







              The @bs attributes are often poorly thought out hacks that you shouldn't expect to work well with other attributes, or really with anything other than exactly what the documentation explains or shows examples of. However, if an attribute is used where it is not intended you'll usually at least get a warning about the attribute being unused, which your code does.



              @bs.string in particular only works on types at the outermost level of externals, i.e. on types whose values will be passed directly to the external function. There is also a way to create JavaScript objects using external functions which also happens to use less magic and give you much more control over the API. As far as I'm aware, the only downside compared to @bs.deriving is that you can't override field names using something like @bs.as. They have to be valid OCaml identifiers.



              Here's your example implemented using an external function annotated with @bs.obj:



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


              To use it you call it exactly as with @bs.deriving:



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


              But even with this I've encountered edge cases where integer values are passed in instead of strings. For this reason I tend to avoid the polymorphic variant attributes altogether and instead use ordinary variants along with conversion functions. This has the added benefit of being more idiomatic, blending in better and being more interoperable with non-BuckleScript code.



              Here's what your example might look like using this approach:



              type color = Blue | Red | Green;
              let colorToString = fun
              | Blue => "blue"
              | Red => "red"
              | Green => "green";

              type other = X | Y;
              let otherToString = fun
              | X => "x"
              | Y => "y";

              [@bs.obj] external options : (
              ~color:string=?,
              ~number:int=?,
              ~other:string=?,
              unit
              ) => options = "";

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

              let func = (~color=?, ~number=?, ~other=?, ()) =>
              func(options(
              ~color = ?Belt.Option.map(color, colorToString),
              ~number?,
              ~other = ?Belt.Option.map(other, otherToString),
              ()));

              let config = func(~color=Blue,~number=123, ~other=X, ());





              share|improve this answer















              The @bs attributes are often poorly thought out hacks that you shouldn't expect to work well with other attributes, or really with anything other than exactly what the documentation explains or shows examples of. However, if an attribute is used where it is not intended you'll usually at least get a warning about the attribute being unused, which your code does.



              @bs.string in particular only works on types at the outermost level of externals, i.e. on types whose values will be passed directly to the external function. There is also a way to create JavaScript objects using external functions which also happens to use less magic and give you much more control over the API. As far as I'm aware, the only downside compared to @bs.deriving is that you can't override field names using something like @bs.as. They have to be valid OCaml identifiers.



              Here's your example implemented using an external function annotated with @bs.obj:



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


              To use it you call it exactly as with @bs.deriving:



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


              But even with this I've encountered edge cases where integer values are passed in instead of strings. For this reason I tend to avoid the polymorphic variant attributes altogether and instead use ordinary variants along with conversion functions. This has the added benefit of being more idiomatic, blending in better and being more interoperable with non-BuckleScript code.



              Here's what your example might look like using this approach:



              type color = Blue | Red | Green;
              let colorToString = fun
              | Blue => "blue"
              | Red => "red"
              | Green => "green";

              type other = X | Y;
              let otherToString = fun
              | X => "x"
              | Y => "y";

              [@bs.obj] external options : (
              ~color:string=?,
              ~number:int=?,
              ~other:string=?,
              unit
              ) => options = "";

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

              let func = (~color=?, ~number=?, ~other=?, ()) =>
              func(options(
              ~color = ?Belt.Option.map(color, colorToString),
              ~number?,
              ~other = ?Belt.Option.map(other, otherToString),
              ()));

              let config = func(~color=Blue,~number=123, ~other=X, ());






              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Mar 25 at 19:55

























              answered Mar 25 at 16:20









              glennslglennsl

              13.7k103352




              13.7k103352












              • It indeed looks like polymorphic variants don't need to be used in those types of examples. Thank you for your response! I'm implementing new binding for randomColor library and used your version, for now: LINK

                – Krzysztof Trzos
                Mar 26 at 16:34











              • @KrzysztofTrzos one thing to be aware of with this approach is that BuckleScript may not be able to optimize out all the conversion functions out. With the polymorphic variants approach, you 'pay for for you use'.

                – Yawar
                Mar 26 at 16:58











              • @Yawar So what is the cost of this solution and what issues can it bring?

                – Krzysztof Trzos
                Mar 26 at 17:36











              • @Yawar I would go even further and say it very likely won't, unless you're doing everything in a single module. But it's at least sound. That's why I use this approach for bs-webapi and bs-fetch, which are intended as low-level bindings and otherwise a good fit for @bs.string had it worked properly. There are other ways of reducing the overhead, however, such as using constants instead of variants like I do in bs-fetch#next, but then you sacrifice exhaustiveness checking... Unfortunately there is no simple answer here.

                – glennsl
                Mar 26 at 17:45











              • Also, if bsc had been a little bit smarter it could have compiled the conversion functions into some pretty compact lookup tables.

                – glennsl
                Mar 26 at 18:02

















              • It indeed looks like polymorphic variants don't need to be used in those types of examples. Thank you for your response! I'm implementing new binding for randomColor library and used your version, for now: LINK

                – Krzysztof Trzos
                Mar 26 at 16:34











              • @KrzysztofTrzos one thing to be aware of with this approach is that BuckleScript may not be able to optimize out all the conversion functions out. With the polymorphic variants approach, you 'pay for for you use'.

                – Yawar
                Mar 26 at 16:58











              • @Yawar So what is the cost of this solution and what issues can it bring?

                – Krzysztof Trzos
                Mar 26 at 17:36











              • @Yawar I would go even further and say it very likely won't, unless you're doing everything in a single module. But it's at least sound. That's why I use this approach for bs-webapi and bs-fetch, which are intended as low-level bindings and otherwise a good fit for @bs.string had it worked properly. There are other ways of reducing the overhead, however, such as using constants instead of variants like I do in bs-fetch#next, but then you sacrifice exhaustiveness checking... Unfortunately there is no simple answer here.

                – glennsl
                Mar 26 at 17:45











              • Also, if bsc had been a little bit smarter it could have compiled the conversion functions into some pretty compact lookup tables.

                – glennsl
                Mar 26 at 18:02
















              It indeed looks like polymorphic variants don't need to be used in those types of examples. Thank you for your response! I'm implementing new binding for randomColor library and used your version, for now: LINK

              – Krzysztof Trzos
              Mar 26 at 16:34





              It indeed looks like polymorphic variants don't need to be used in those types of examples. Thank you for your response! I'm implementing new binding for randomColor library and used your version, for now: LINK

              – Krzysztof Trzos
              Mar 26 at 16:34













              @KrzysztofTrzos one thing to be aware of with this approach is that BuckleScript may not be able to optimize out all the conversion functions out. With the polymorphic variants approach, you 'pay for for you use'.

              – Yawar
              Mar 26 at 16:58





              @KrzysztofTrzos one thing to be aware of with this approach is that BuckleScript may not be able to optimize out all the conversion functions out. With the polymorphic variants approach, you 'pay for for you use'.

              – Yawar
              Mar 26 at 16:58













              @Yawar So what is the cost of this solution and what issues can it bring?

              – Krzysztof Trzos
              Mar 26 at 17:36





              @Yawar So what is the cost of this solution and what issues can it bring?

              – Krzysztof Trzos
              Mar 26 at 17:36













              @Yawar I would go even further and say it very likely won't, unless you're doing everything in a single module. But it's at least sound. That's why I use this approach for bs-webapi and bs-fetch, which are intended as low-level bindings and otherwise a good fit for @bs.string had it worked properly. There are other ways of reducing the overhead, however, such as using constants instead of variants like I do in bs-fetch#next, but then you sacrifice exhaustiveness checking... Unfortunately there is no simple answer here.

              – glennsl
              Mar 26 at 17:45





              @Yawar I would go even further and say it very likely won't, unless you're doing everything in a single module. But it's at least sound. That's why I use this approach for bs-webapi and bs-fetch, which are intended as low-level bindings and otherwise a good fit for @bs.string had it worked properly. There are other ways of reducing the overhead, however, such as using constants instead of variants like I do in bs-fetch#next, but then you sacrifice exhaustiveness checking... Unfortunately there is no simple answer here.

              – glennsl
              Mar 26 at 17:45













              Also, if bsc had been a little bit smarter it could have compiled the conversion functions into some pretty compact lookup tables.

              – glennsl
              Mar 26 at 18:02





              Also, if bsc had been a little bit smarter it could have compiled the conversion functions into some pretty compact lookup tables.

              – glennsl
              Mar 26 at 18:02











              0














              This is because in reality, those values are variants, instead of trying to make it exactly like JavaScript, I would rather try something more idiomatic to Reason:



              type color = Blue | Green | Red;
              type coords = X | Y;
              type config =
              color,
              coords,
              number: int
              ;

              let func = (config: config) => "something"


              And then inside your function actually return strings (if that is what you really need) by pattern matching on the correct values provided to config.



              See the working code here.



              Hope it helps!






              share|improve this answer



























                0














                This is because in reality, those values are variants, instead of trying to make it exactly like JavaScript, I would rather try something more idiomatic to Reason:



                type color = Blue | Green | Red;
                type coords = X | Y;
                type config =
                color,
                coords,
                number: int
                ;

                let func = (config: config) => "something"


                And then inside your function actually return strings (if that is what you really need) by pattern matching on the correct values provided to config.



                See the working code here.



                Hope it helps!






                share|improve this answer

























                  0












                  0








                  0







                  This is because in reality, those values are variants, instead of trying to make it exactly like JavaScript, I would rather try something more idiomatic to Reason:



                  type color = Blue | Green | Red;
                  type coords = X | Y;
                  type config =
                  color,
                  coords,
                  number: int
                  ;

                  let func = (config: config) => "something"


                  And then inside your function actually return strings (if that is what you really need) by pattern matching on the correct values provided to config.



                  See the working code here.



                  Hope it helps!






                  share|improve this answer













                  This is because in reality, those values are variants, instead of trying to make it exactly like JavaScript, I would rather try something more idiomatic to Reason:



                  type color = Blue | Green | Red;
                  type coords = X | Y;
                  type config =
                  color,
                  coords,
                  number: int
                  ;

                  let func = (config: config) => "something"


                  And then inside your function actually return strings (if that is what you really need) by pattern matching on the correct values provided to config.



                  See the working code here.



                  Hope it helps!







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Mar 25 at 12:46









                  KutyelKutyel

                  4,89912449




                  4,89912449



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55325408%2freasonml-binding-function-with-config-having-fixed-string-values%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

                      Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

                      Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript