来自 javascript 绑定的未标记联合走错了路
Untagged union from a javascript binding going down the wrong path
我正在尝试为 amqplib npm 包编写 reasonml 绑定:
http://www.squaremobius.net/amqp.node/
特别是这个函数:
http://www.squaremobius.net/amqp.node/channel_api.html#channel_get
class type amqpMessageT = [@bs] {
pub content: nodeBuffer
};
type amqpMessage = Js.t(amqpMessageT);
type gottenMessage = Js.Nullable.t(amqpMessage);
type qualifiedMessage = Message(gottenMessage) | Boolean(bool);
class type amqpChannelT = [@bs] {
pub assertQueue: string => queueParams => Js.Promise.t(unit);
pub consume: string => (amqpMessage => unit) => unit;
pub ack: amqpMessage => unit;
pub get: string => Js.Promise.t(qualifiedMessage);
pub purgeQueue: string => Js.Promise.t(unit);
pub deleteQueue: string => Js.Promise.t(unit);
pub sendToQueue: string => nodeBuffer => messageParams => unit;
};
然后我有以下代码:
....
channel##get("MyQueue")
|> Js.Promise.then_(message => {
switch message {
| Boolean(false) => Js.Promise.resolve(Js.log("No Message"));
| Message(msg) => Js.Promise.resolve(Js.log("Has Message, Will Travel"));
| Boolean(true) => Js.Promise.resolve(Js.log("Impossible Message"!));
}
}
然而,这会沿着 "Message(msg)" 路径 always,即使 js 调用 returns false。
现在添加以下绑定:
let unsafeGet: amqpChannel => string => Js.Promise.t(gottenMessage) = [%bs.raw{|function(channel, queueName) {
return channel.get(queueName).then((value) => {
if(value === false) {
return Promise.resolve(null)
} else {
return Promise.resolve(value)
}
})
}|}];
我已经能够回避这个问题,但老实说,我不太喜欢使用 bs.raw
。我最初的未标记联合类型有什么问题?我该如何解决这个问题?
OCaml 语言中没有未标记的联合类型,也没有 运行 时间类型信息,因此您必须实现自己的类型、检查和转换以使其成为可用的形式。
例如,您可以使用抽象类型来表示 "unknown" 类型,并使用伴随函数来检查其类型,将其转换为该类型,然后将其转换为 qualifiedMessage
:
type unknownMessage;
let classifyMessage = (value: unknownMessage) =>
switch (Js.Types.classify(value)) {
| JSString(s) => Message(Js.Nullable.return(s))
| JSNull => Message(Js.null)
| JSFalse => Boolean(false)
| JSTrue => Boolean(true)
| _ => failwith("invalid runtime type")
}
此外,作为旁注,如果您通过公开抽象类型和 functions/externals 而不是公开 "raw" 对象来抽象出底层数据结构,您将获得很大的灵活性定义接口并可以隐藏这个额外的转换步骤。
我正在尝试为 amqplib npm 包编写 reasonml 绑定:
http://www.squaremobius.net/amqp.node/
特别是这个函数:
http://www.squaremobius.net/amqp.node/channel_api.html#channel_get
class type amqpMessageT = [@bs] {
pub content: nodeBuffer
};
type amqpMessage = Js.t(amqpMessageT);
type gottenMessage = Js.Nullable.t(amqpMessage);
type qualifiedMessage = Message(gottenMessage) | Boolean(bool);
class type amqpChannelT = [@bs] {
pub assertQueue: string => queueParams => Js.Promise.t(unit);
pub consume: string => (amqpMessage => unit) => unit;
pub ack: amqpMessage => unit;
pub get: string => Js.Promise.t(qualifiedMessage);
pub purgeQueue: string => Js.Promise.t(unit);
pub deleteQueue: string => Js.Promise.t(unit);
pub sendToQueue: string => nodeBuffer => messageParams => unit;
};
然后我有以下代码:
....
channel##get("MyQueue")
|> Js.Promise.then_(message => {
switch message {
| Boolean(false) => Js.Promise.resolve(Js.log("No Message"));
| Message(msg) => Js.Promise.resolve(Js.log("Has Message, Will Travel"));
| Boolean(true) => Js.Promise.resolve(Js.log("Impossible Message"!));
}
}
然而,这会沿着 "Message(msg)" 路径 always,即使 js 调用 returns false。
现在添加以下绑定:
let unsafeGet: amqpChannel => string => Js.Promise.t(gottenMessage) = [%bs.raw{|function(channel, queueName) {
return channel.get(queueName).then((value) => {
if(value === false) {
return Promise.resolve(null)
} else {
return Promise.resolve(value)
}
})
}|}];
我已经能够回避这个问题,但老实说,我不太喜欢使用 bs.raw
。我最初的未标记联合类型有什么问题?我该如何解决这个问题?
OCaml 语言中没有未标记的联合类型,也没有 运行 时间类型信息,因此您必须实现自己的类型、检查和转换以使其成为可用的形式。
例如,您可以使用抽象类型来表示 "unknown" 类型,并使用伴随函数来检查其类型,将其转换为该类型,然后将其转换为 qualifiedMessage
:
type unknownMessage;
let classifyMessage = (value: unknownMessage) =>
switch (Js.Types.classify(value)) {
| JSString(s) => Message(Js.Nullable.return(s))
| JSNull => Message(Js.null)
| JSFalse => Boolean(false)
| JSTrue => Boolean(true)
| _ => failwith("invalid runtime type")
}
此外,作为旁注,如果您通过公开抽象类型和 functions/externals 而不是公开 "raw" 对象来抽象出底层数据结构,您将获得很大的灵活性定义接口并可以隐藏这个额外的转换步骤。