# Nesting Algebraic Handlers found in Different Modules

**URL:** <https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256>\
**Category:** Learning\
**Tags:** effects, error-handling, algebraic-effects\
**Created:** [March 6, 2024, 7:53am UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256 "2024-03-06T07:53:26Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Marietta\_Galea](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/marietta_galea/32/5172_2.png) [@Marietta\_Galea](https://discuss.ocaml.org/u/Marietta_Galea)\
**Post date:** [March 6, 2024, 7:53am UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/1 "2024-03-06T07:53:26Z")

</div>

Can algebraic handlers in OCaml be separated and put into different modules, then be nested in some other file?

For instance, say there are two compilation units `Handler1` and `Handler2`, each with their ml and mli files. They each contain some effect E and have a function that handles the effect, something like:

```auto
let handlerE prog () = match_with prog () { ... handler ... } (* in Handler1*)
let handlerF prog () = match_with prog () { ... handler ... } (* in Handler2*)

```

In another file, `run_handlers.ml`, can these two handlers be nested in some way? Will the effects in `prog` go unhandled if we do so, contrary to what would happen if the handlers all in the same file?

---

<div class="post-metadata">

**Author:** ![octachron](https://avatars.discourse-cdn.com/v4/letter/o/49beb7/32.png) [@octachron](https://discuss.ocaml.org/u/octachron)\
**Post date:** [March 7, 2024, 2:11pm UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/2 "2024-03-07T14:11:53Z")

</div>

Yes, effects and handlers can be defined independently in different modules. In fact, splitting the definitions across two module should not change anything.

For instance, the toy example below work as expected by nesting the `choice` handler under the `rand` handler:

```ocaml
module D = Effect.Deep

module Rand = struct
   type _ Effect.t +=
     | Bool: bool Effect.t
     | Int : int -> int Effect.t
  let bool () = Effect.perform Bool
  let int bound = Effect.perform (Int bound)
  let handle f x=
    let effc (type a r) (e:a Effect.t): ((a,r) D.continuation -> r) option =
      match e with
      | Int n -> Some (fun k -> D.continue k @@ Random.int n)
      | Bool -> Some (fun k -> D.continue k @@ Random.bool ())
      | _ -> None
    in
    Effect.Deep.match_with f x { retc = Fun.id; exnc = raise; effc }

  let mock f x=
    let effc (type a r) (e:a Effect.t): ((a,r) D.continuation -> r) option =
      match e with
      | Int n -> Some (fun k -> D.continue k 0)
      | Bool -> Some (fun k -> D.continue k true)
      | _ -> None
    in
    Effect.Deep.match_with f x { retc = Fun.id; exnc = raise; effc }

end

module Choice = struct
  type _ Effect.t +=
   | Int : int * int -> int Effect.t
  let int x y = Effect.perform (Int(x,y))
  let handle f x =
    let effc (type a r) (e:a Effect.t): ((a,r) D.continuation -> r) option =
      match e with
      | Int (x,y) ->
        Some (fun k ->
            if Rand.bool ()
            then D.continue k x
            else D.continue k y
          )
      | _ -> None
    in
    Effect.Deep.match_with f x { retc = Fun.id; exnc = raise; effc }
end

let test () = Rand.int 5 + Choice.int 1 2

let () =
  Format.printf "@[<v>mock result=%d,@ result=%d@]@."
    (Rand.mock (Choice.handle test) ())
    (Rand.handle (Choice.handle test) ())

```

---

<div class="post-metadata">

**Author:** ![Marietta\_Galea](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/marietta_galea/32/5172_2.png) [@Marietta\_Galea](https://discuss.ocaml.org/u/Marietta_Galea)\
**Post date:** [March 9, 2024, 9:01am UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/3 "2024-03-09T09:01:04Z")

</div>

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

```ocaml
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:

```auto
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:

```auto
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:

```ocaml
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:

```auto
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.

---

<div class="post-metadata">

**Author:** ![octachron](https://avatars.discourse-cdn.com/v4/letter/o/49beb7/32.png) [@octachron](https://discuss.ocaml.org/u/octachron)\
**Post date:** [March 9, 2024, 9:19am UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/4 "2024-03-09T09:19:11Z")

</div>

> [@Marietta\_Galea](#):
>
> `let run_handlers () = Handler2.run_h2(Handler1.run_h1 ()) ()`

There is your error, `Handler1.run_h1 ()` is evaluated outside of the `run_h2` handler function, you meant

```ocaml
let run_handlers () = Handler2.run_h2 Handler1.run_h1 ()

```

---

<div class="post-metadata">

**Author:** ![Marietta\_Galea](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/marietta_galea/32/5172_2.png) [@Marietta\_Galea](https://discuss.ocaml.org/u/Marietta_Galea)\
**Post date:** [March 9, 2024, 11:03am UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/5 "2024-03-09T11:03:18Z")

</div>

I understand what you’re saying and it makes sense, so thank you very much for that.

When I tried out this alteration in my code, it seems that I have to change some types since now `run_h2` is given `Handler1.run_h1` as an argument. Is the type of this `unit -> unit` or is it `unit -> (unit -> unit)`? I am struggling a bit to understand what the return type of `run_h1` is in this case. I am trying to follow the static semantics explanation given in [Retrofitting Effect Handlers onto OCaml](https://arxiv.org/abs/2104.00250).

---

<div class="post-metadata">

**Author:** ![octachron](https://avatars.discourse-cdn.com/v4/letter/o/49beb7/32.png) [@octachron](https://discuss.ocaml.org/u/octachron)\
**Post date:** [March 9, 2024, 11:35am UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/6 "2024-03-09T11:35:13Z")

</div>

Hm, you are right; I missed the fact that you had a superfluous `()` in the definition of `h1`.

In this case, your code example works without any troubles.

The issue is elsewhere (and has nothing to do with modules).

---

<div class="post-metadata">

**Author:** ![monoidoid](https://avatars.discourse-cdn.com/v4/letter/m/ecc23a/32.png) [@monoidoid](https://discuss.ocaml.org/u/monoidoid)\
**Post date:** [March 9, 2024, 1:56pm UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/7 "2024-03-09T13:56:53Z")

</div>

It would help to see all of the code.

My only guess is the effect constructors aren’t shared (doing `Effect.t += Report` in module A and another `Effect.t += Report` in module B will result in distinct constructors `A.Report` and `B.Report`, you want to have just one `Effect.t += Report` in a shared module).

---

<div class="post-metadata">

**Author:** ![Marietta\_Galea](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/marietta_galea/32/5172_2.png) [@Marietta\_Galea](https://discuss.ocaml.org/u/Marietta_Galea)\
**Post date:** [March 10, 2024, 10:55am UTC](https://discuss.ocaml.org/t/nesting-algebraic-handlers-found-in-different-modules/14256/8 "2024-03-10T10:55:45Z")

</div>

It seems that since the effects were declared in separate files, they were indeed different constructors. Once I put the effects in one module and shared that amongst `Handler1` and `Handler2`, then the problem was fixed. Thanks!
