Exception
异常只是一种特殊类型的变体, 在 异常 的情况下 "抛出" (请不要滥用!)
用法
let getItem = (theList) =>
if (callSomeFunctionThatThrows()) {
/* 在这里返回找到的值 */
} else {
raise(Not_found)
};
let result =
try (getItem([1, 2, 3])) {
| Not_found => 0 /* getitem 异常情况下的默认值 */
};
注意上述只是为了展示;实际上,你应该直接从 getItem
返回 option(int)
而避免和 try
一起使用。
在从一个函数获得返回值的 同时,也可以直接匹配异常:
switch (List.find((i) => i === theItem, myItems)) {
| item => print_endline(item)
| exception Not_found => print_endline("No such item found!")
};
你也可以像创建 variant 一样创建异常(异常也必須是字母大写)。
exception InputClosed(string);
...
raise(InputClosed("the stream has closed!"));
提示 & 技巧
一般使用 variant 时,几乎 不需要 用到异常。 例如,在集合中找不到 item
时,试着返回 option(item)
(这个例子返回None
),而不是抛出异常。
设计决策
上述提示看似和 OCaml 标准库的行为有所相悖;模块中著名的函数如 List 和 String ,看上去过于频繁使用异常了。 这些部分是历史沉淀的结果,部分则出于对性能的考虑。 原生 OCaml/Reason 是非常有效率;抛出异常被设计的很轻量,比分配和返回值还要轻量,例如 option
。 很遗憾这和 JavaScript 没什么关系。
较新的标准库通常返回 option
,而非抛出异常。 例如,List.find
返回和 option
类似的 List.find_opt
,而不会抛出异常。
异常实际上也就是变体(variant)。 事实上,它们都属于同一个变体类型,叫做 exn
。 这是 可扩展变体,表示你可以加入新的构造器,例如上述的 InputClosed
。 exception Foo
只是个語法糖,方便加入构造函数到 exn
。