For instance, say you have a program in a file called program.ml
that uses a user-defined function put: int -> unit
that modifies a variable x
. In this scenario, there is an effect Put_found: int -> unit Effect.t
and put
is defined as
put n = x:= n; perform(Put_found n)
A sample program would look like: put(5);put(0)
.
There are also two compilation units Handler1
and Handler2
, each with their ml and mli files. The former contains a function run_h1
that when given the sample program as input, the handler acts on the effect Put_found
by printing the integer passed to it. Then, it raises another effect Report_p
, which is to be passed to the second handler. run_h1
returns a function of type unit -> unit
as looks like this:
let run_h1 () = fun () -> match_with Program.comp () {
effc = (fun (type c) (eff: c Effect.t) ->
match eff with
| Put_found s -> Some (fun (k : (c,_) continuation) ->
report_p(s); continue k ())
| _ -> None
);
}
The second effect, Report_p
of type int -> unit Effect.t
, is to be handled by a function found in Handler2
such that if the integer passed is negative, an error message is printed to the screen.
run_h2
is the following function:
let run_h2 inst x = match_with inst x
{
effc = (fun (type b) (eff: b Effect.t) ->
match eff with
| Report n -> Some (fun (k: (b,_) continuation) ->
if n = (-1) then printf "Put with value -1 encountered.\n" else printf "Put found with allowed value.\n"; continue k ())
| _ -> None
);
exnc = raise;
retc = fun x-> x
}
In another file, run_handlers.ml
, I make use of the compilation units mentioned above as follows:
let run_handlers () = Handler2.run_h2(Handler1.run_h1 ()) ()
When at first the program and effects were found in the same file and the handlers were nested in the same file, the program worked fine. The program looked like this:
let run_h2 () =
let comp () = put(5); put(6); put(-1); put(100) in
let run_h1 () = match_with comp ()
{ effc = (fun (type c) (eff: c Effect.t) ->
match eff with
| Put_found s -> Some (fun (k: (c,_) continuation) ->
report_p(s); continue k ())
| _ -> None
);
exnc = raise;
retc = fun x-> x
} in
match_with run_h1 () {
effc = (fun (type b) (eff: b Effect.t) ->
match eff with
| Report n -> Some (fun (k: (b,_) continuation) ->
if n = (-1) then printf "Put with value -1 encountered.\n" else printf "Put found with value: %d\n" (n); continue k ())
| _ -> None
);
exnc = raise;
retc = fun x-> x
}
Now, when trying to modularise it as explained above, a Stdlib.Effect.Unhandled
exception was thrown on the first instance the effect should be performed.