Given the following types:
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)}
I get an error from the compiler:
Error: This expression has type b#1 stack but an expression was expected of type 'a stack The type constructor b#1 would escape its scope
In this line of code:
| 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
It's taken a while to get this far without running into an obscure error message that is inevitably corrected by using some helper types, but I can't seem to figure out how to correct this issue with a helper, or if I'm attempting to do something silly with my types.
What is the correct way to eliminate this error?
A second variable needed to be added to the task type definition to express separate success and failure values. Here is the complete solution:
I think there is something incoherent in these functions. Adding some annotations and removing irrelevant branches gives:
where the "underlined" variable is the subject of an error message:
What is supposed to happen if the first pass through
step
operates on an(OnErrorStack : unit stack)
, and then the second pass throughstep
operates on an(AndThenStack : int stack)
?In other words, if the argument to
loop
is something like:While
(value : unit)
will be compatible with the firststep
, it seems to me that nothing guarantees its compatibility with the secondstep
, which acts rather on a value of the existential type within theOnErrorStack
(int
in the counter-example).