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, но ожидалось выражение типа стек. Конструктор типа 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
Потребовалось некоторое время, чтобы пройти этот путь, не сталкиваясь с неясным сообщением об ошибке, которое неизбежно исправляется с помощью некоторых типов помощников, но я не могу понять, как исправить эту проблему с помощью помощника, или если я пытаюсь сделать что-нибудь глупое с моими типами.
Как правильно устранить эту ошибку?
2 ответа
Вторая переменная должна быть добавлена к определению типа задачи, чтобы выразить отдельные значения успеха и ошибки. Вот полное решение:
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)}
Я думаю, что есть что-то непоследовательное в этих функциях. Добавление некоторых аннотаций и удаление ненужных веток дает:
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
в контр-примере).