OCaml 多态递归错误



给定以下类型:

type _ task =
| Success : 'a -> 'a task
| Fail : 'a -> 'a task
| Binding : (('a task -> unit) -> unit) -> 'a task
| AndThen : ('a -> 'b task) * 'a task -> 'b task
| OnError : ('a -> 'b task) * 'a task -> 'b task
type _ stack =
| NoStack : 'a stack
| AndThenStack : ('a -> 'b task) * 'b stack -> 'a stack
| OnErrorStack : ('a -> 'b task) * 'b stack -> 'a stack
type 'a process = 
{ root: 'a task 
; stack: 'a stack 
}
let rec loop : 'a. 'a process -> unit = fun proc ->
match proc.root with
| Success value -> 
let rec step = function
| NoStack -> ()
| AndThenStack (callback, rest) -> loop {proc with root = callback value; stack = rest }
| OnErrorStack (_callback, rest) -> step rest  <-- ERROR HERE
in
step proc.stack
| Fail value -> 
let rec step = function
| NoStack -> ()
| AndThenStack (_callback, rest) -> step rest
| OnErrorStack (callback, rest) -> loop {proc with root = callback value; stack = rest }
in
step proc.stack
| Binding callback -> callback (fun task -> loop {proc with root = task} )
| AndThen (callback, task) -> loop {root = task; stack = AndThenStack (callback, proc.stack)}
| OnError (callback, task) -> loop {root = task; stack = OnErrorStack (callback, proc.stack)}

我从编译器收到错误:

错误:此表达式的类型为 b#1 堆栈 但是需要类型'a堆栈"的表达式 类型构造函数 b#1 将逃脱其范围

在以下代码行中:

| Success value -> 
let rec step = function
| NoStack -> ()
| AndThenStack (callback, rest) -> loop {proc with root = callback value; stack = rest }
| OnErrorStack (_callback, rest) -> step rest  <-- ERROR HERE
in
step proc.stack

到目前为止,没有遇到一个晦涩的错误消息需要一段时间,该错误消息不可避免地通过使用一些帮助程序类型来纠正,但我似乎无法弄清楚如何使用帮助程序纠正这个问题,或者我是否试图用我的类型做一些愚蠢的事情。

消除此错误的正确方法是什么?

我认为这些函数中有一些不连贯的地方。添加一些注释并删除不相关的分支会得到:

let rec loop (type s) (proc : s process) =
match proc.root with
| Success value -> 
let rec step (type t) (x : t stack) =
match x with
| NoStack -> ()
| AndThenStack (callback, rest) ->
loop {proc with root = callback value; stack = rest }
(*^^^^^*)
| OnErrorStack (callback, rest) -> step rest
in
step proc.stack
| _ -> ()

其中"带下划线"变量是错误消息的主题:

错误:此表达式的类型为 s,但表达式的类型应为 t

如果第一次通过step(OnErrorStack : unit stack)上运行,然后第二次通过step(AndThenStack : int stack)上运行,会发生什么?

换句话说,如果loop的参数是这样的:

{ root = Success ();
stack = OnErrorStack ((fun () -> Success 3),
AndThenStack ((fun x -> Success (float_of_int x)),
(NoStack : float stack))) }

虽然(value : unit)与第一个step兼容,但在我看来,没有什么能保证它与第二个step的兼容性,后者作用于OnErrorStack内存在类型的值(int在反例中(。

需要将第二个变量添加到任务类型定义中,以表示单独的成功和失败值。以下是完整的解决方案:

type (_,_) task =
| Success : 'a -> ('a,_) task
| Fail : 'x -> (_,'x) task
| Binding : ((('a,'x) task -> unit) -> unit) -> ('a,'x) task
| AndThen : ('a -> ('b,'x) task) * ('a,'x) task -> ('b,'x) task
| OnError : ('x -> ('a,'y) task) * ('a,'x) task -> ('a,'y) task
type (_,_) stack =
| NoStack : (_,_) stack
| AndThenStack : ('a -> ('b,'x) task) * ('b,'x) stack -> ('a,'x) stack
| OnErrorStack : ('x -> ('a,'y) task) * ('a,'y) stack -> ('a,'x) stack
type ('a,'x) process = 
{ root: ('a,'x) task 
; stack: ('a,'x) stack 
}
let rec loop : type a x. (a, x) process -> unit = fun proc ->
match proc.root with
| Success value -> 
let rec step : 'x. (a, 'x) stack -> unit = function
| NoStack -> ()
| AndThenStack (callback, rest) -> loop {root = callback value; stack = rest }
| OnErrorStack (_callback, rest) -> step rest
in
step proc.stack
| Fail value -> 
let rec step : 'a. ('a, x) stack -> unit = function
| NoStack -> ()
| AndThenStack (_callback, rest) -> step rest
| OnErrorStack (callback, rest) -> loop {root = callback value; stack = rest }
in
step proc.stack
| Binding callback -> callback (fun task -> loop {proc with root = task})
| AndThen (callback, task) -> loop {root = task; stack = AndThenStack (callback, proc.stack)}
| OnError (callback, task) -> loop {root = task; stack = OnErrorStack (callback, proc.stack)}

最新更新