带配置的ReasonML绑定功能具有固定的字符串值

时间:2019-03-24 15:31:23

标签: ffi reason bucklescript

可以说,我在Javascript中具有此功能,可以根据适当的配置生成字符串:

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

还要假设config变量具有以下结构(所有这些都不能提供给函数调用):

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

如何为此创建适当的绑定?我坚持:

[@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";

但是在尝试像这样使用时不起作用:

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

let value = MyModule.func(config);

colorother的值是整数,而不是字符串。

3 个答案:

答案 0 :(得分:1)

@bs属性通常是经过深思熟虑的骇客,您不应期望它们与其他属性或与文档解释或显示的示例完全不同的其他属性很好地工作。但是,如果在非预期的地方使用了属性,通常至少会得到关于该属性未使用的警告,您的代码也会这样做。

@bs.string特别仅适用于外部最外部的类型,即,其值将直接传递给外部函数的类型。还有一种方法可以使用外部函数来创建JavaScript对象,这些函数也可以减少魔术的使用并为您提供对API的更多控制。据我所知,与@bs.deriving相比,唯一的缺点是您无法使用@bs.as之类的名称来覆盖字段名称。它们必须是有效的OCaml标识符。

这是您的示例,该示例使用带有@bs.obj注释的外部函数来实现:

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

要使用它,请使用与@bs.deriving完全相同的名称进行调用:

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

但是即使这样,我也遇到了一些极端情况,即传入整数值而不是字符串。因此,我倾向于完全避免使用多态变体属性,而是将普通变体与转换函数一起使用。这样做还有一个好处,就是更加惯用,更好地融合并且可以与非BuckleScript代码进行互操作。

使用这种方法,您的示例如下所示:

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

答案 1 :(得分:1)

这是命名参数(具有可选字段的对象)的JavaScript惯用语的一种情况,需要适应OCaml / ReasonML惯用语(具有实际标记参数的函数)。您将分三步执行此操作。如Glenn所示,第1步为配置定义外部:

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

第2步,使用配置对象的JavaScript样式绑定到JavaScript函数:

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

第3步,将JavaScript函数绑定包装在带有标签参数的OCaml惯用函数中:

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

您可以像这样使用它:

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

答案 2 :(得分:0)

这是因为实际上,这些值是 variants ,而不是试图使其完全类似于JavaScript,我宁愿尝试一些更符合习俗的原因:

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

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

然后,在函数内部实际上通过对提供给config的正确值进行模式匹配来返回字符串(如果您真正需要的是)。

请参见工作代码here

希望有帮助!