Am I wrong about Effects? I see them as a step back

One thing I worry about a little (if effects and their handlers become more widespread) is the complexity of composing handlers. Of course this might be a little easier to catch bugs with an effect system, but I still think it might be quite complicated for a user.

This is particularly problematic when effects are performed inside other handlers either as part of the implementation of the handler or because the effect runs a user-defined function (e.g. fork in Eio). To take the HashDirectory example:

module Sha = struct
  type _ Effect.t += HashDirectory : Fs.dir Path.t -> string Effect.t

  let hash_dir dir = Effect.perform (HashDirectory dir)

  let local_handler fn =
    (* This doesn't actually do the hash, just performs an effect! *)
    try_with fn () {
      effc = fun (type a) (e : a Effect.t) -> match e with
        | HashDirectory path -> Some (fun (k : (a, _) continuation) -> 
          let first = Path.(path / (List.hd @@ Path.read_dir path)) in
          continue k (Path.load first)
        )
        | _ -> None
    }
end

let () =
  Eio_main.run @@ fun env ->        (* 1 *)
  Sha.local_handler @@ fun () ->    (* 2 *)
  let hash1 = Sha.hash_dir env#cwd in
  let hash2 =
    Switch.run @@ fun sw ->
    Eio.Promise.await_exn @@ Fiber.fork_promise ~sw (fun () -> Sha.hash_dir env#cwd)
  in
  assert (hash1 = hash2)

In this (maybe a little contrived) example there is no ordering of the handlers at 1 and 2 that allows this program to run. In the way it is currently written, the hash_dir inside the fork escapes the scope of the local_handler. If you were to swap the handler around then the effects performed in the implementation of the local_handler (by using Eio functions) would escape the scope of the Eio handler! The programmer has to decide what kind of program they want by ordering the handlers.

I also worry about, but have very little knowledge of, how an effect system would track such a program. Will it know that the function called in fork better not perform a HashDirectory effect because the Eio handler is outside the scope of the local_handler? Is that an easy thing to track? I really have no idea ^^"

But let’s also appreciate the direct-style of it all :)) I think I’m heading towards the camp of limiting my use of effects to only the bare essentials (at the moment, for async IO).

3 Likes