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;
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
add a comment |
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
There's several bugs in your example code: 1. There's a missing comma at the end of thecolor
field definition, and 2. The call toMyModule.config
, which technically also should beMyModule.options
, is partially applied because it's missing the finalunit
argument.
– glennsl
Mar 25 at 16:02
add a comment |
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
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
ffi reason bucklescript
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 thecolor
field definition, and 2. The call toMyModule.config
, which technically also should beMyModule.options
, is partially applied because it's missing the finalunit
argument.
– glennsl
Mar 25 at 16:02
add a comment |
There's several bugs in your example code: 1. There's a missing comma at the end of thecolor
field definition, and 2. The call toMyModule.config
, which technically also should beMyModule.options
, is partially applied because it's missing the finalunit
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
add a comment |
3 Answers
3
active
oldest
votes
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, ());
add a comment |
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, ());
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 forbs-webapi
andbs-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
|
show 3 more comments
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!
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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, ());
add a comment |
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, ());
add a comment |
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, ());
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, ());
edited Mar 27 at 18:46
answered Mar 25 at 18:00
YawarYawar
7,24933758
7,24933758
add a comment |
add a comment |
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, ());
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 forbs-webapi
andbs-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
|
show 3 more comments
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, ());
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 forbs-webapi
andbs-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
|
show 3 more comments
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, ());
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, ());
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 forbs-webapi
andbs-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
|
show 3 more comments
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 forbs-webapi
andbs-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
|
show 3 more comments
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!
add a comment |
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!
add a comment |
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!
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!
answered Mar 25 at 12:46
KutyelKutyel
4,89912449
4,89912449
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 toMyModule.config
, which technically also should beMyModule.options
, is partially applied because it's missing the finalunit
argument.– glennsl
Mar 25 at 16:02