在OCaml中,可以定义自己的异常,并且这些异常可以接受参数,如以下代码片段所示。
exception there_is_a_problem of string
我想知道是否有一种方法可以使用参数中多态的异常。一个示例应用程序是快捷方式遍历数据结构。例如,我希望能够按照以下几行写一些东西。
exception Found_it of 'a
let find_opt test l =
let aux elt = if test elt then raise (Found_it elt) in
try List.iter aux l; None with
| Foundit b -> Some b
我的实际数据结构比列表更复杂,我更喜欢使用迭代器遍历它,因此我无法像stdlib find_opt
一样编写List.find_opt
。我当前的解决方案是使用如下参考。我发现上述样式更加优雅,但现在我只是觉得好奇。我要解决的另一个解决方案是定义一个新的通用折叠迭代器,如果满足某些输入测试,该迭代器可以缩短计算速度,但这需要访问数据结构的实现。
let find_opt' test l =
let store = ref None in
let aux elt = if test elt then (store := Some elt; raise Exit) in
(try List.iter aux l with Exit -> ());
!store
此代码似乎与您要的内容非常接近:
let f (type a) test (l: a list) =
let module M = struct exception F of a end in
let aux elt = if test elt then raise (M.F elt) in
try List.iter aux l; None
with M.F b -> Some b
可能有一种更简单的方法,但这是我想出的。
更新
此代码使用本地抽象类型,在Section 8.5 of the OCaml manual中有描述。
虽然现在正在阅读手册,但我发现它建议针对您所要求的情况使用局部抽象类型!这使我更有信心,我的回答非常好:-)
另一种选择是使用with_return
函数(例如Base有一个):
let find_opt' test l =
with_return ( fun {return} ->
List.iter (fun elt -> if test elt then return (Some elt)) l; None
)
技巧是使with_return
函数定义一个新的异常(具有局部抽象),并提供一个向用户引发新异常的多态函数:
type 'a return = { return: 'never. 'a -> 'never }
let with_return (type a) f =
let exception Return of a in
let return x = raise (Return x) in
try f {return} with Return x -> x
return
类型可能看起来很奇怪,但是它表示一个事实,即内部return
函数从不返回当前上下文,并且始终引发异常。由于如此精确,因此可以在更多上下文中使用return函数。例如,在下面的示例中
let l = with_return (fun {return} ->
let integer = if test () then return () else 1 in
let list = if test () then return () else [1] in
integer :: list
)
return
首先在期望返回int
的上下文中使用,然后在期望int list
的上下文中使用。如果没有显式多态注释,这将是类型错误。