ocaml-community / utop

Universal toplevel for OCaml
Other
833 stars 110 forks source link

Ocaml 5 Effects tutorial doesn't seem to work #450

Closed andrewray closed 11 months ago

andrewray commented 12 months ago

I was trying to work through the Ocaml 5.0 effects tutorial at https://github.com/ocaml-multicore/ocaml-effects-tutorial#1-algebraic-effects-and-handlers however, the first example doesn't work in utop - the effect is not caught, though the handler is run.

Not sure if this is a known issue - I couldn't find anything related.

Octachron commented 12 months ago

I don't have any issue with running the first example in utop. How did you run the example?

andrewray commented 11 months ago

I was mucking around in the top level and defined things multiple times. If you paste the whole example in a second time, it doesn't work (or at least doesn't for me...).

I've tried to come up with something simpler but couldn't yet.

emillon commented 11 months ago

I could reproduce. The issue is with example 1.2:

open Effect
open Effect.Deep

type _ Effect.t += Conversion_failure : string -> int Effect.t

let int_of_string l =
  try int_of_string l with
  | Failure _ -> perform (Conversion_failure l)

let rec sum_up acc =
    let l = input_line stdin in
    acc := !acc + int_of_string l;
    sum_up acc

let _ =
  Printf.printf "Starting up. Please input:\n%!";
  let r = ref 0 in
  match_with sum_up r
  { effc = (fun (type c) (eff: c Effect.t) ->
      match eff with
      | Conversion_failure s -> Some (fun (k: (c,_) continuation) ->
              Printf.fprintf stderr "Conversion failure \"%s\"\n%!" s;
              continue k 0)
      | _ -> None
    );
    exnc = (function
        | End_of_file -> Printf.printf "Sum is %d\n" !r
        | e -> raise e
    );
    (* Shouldn't reach here, means sum_up returned a value *)
    retc = fun _ -> failwith "Impossible, sum_up shouldn't return"
  }

Running it the second twice shows an error:

Starting up. Please input:
10
20
MM
Exception: Stdlib.Effect.Unhandled(Conversion_failure("MM"))
Called from Topeval.load_lambda in file "toplevel/byte/topeval.ml", line 89, characters 4-14

Is this the issue you were seeing?

Octachron commented 11 months ago

The issue is with the repeated definition of int_of_string:

let int_of_string l =
  try int_of_string l with
  | Failure _ -> perform (Conversion_failure l)

type _ Effect.t += Conversion_failure : string -> int Effect.t

let int_of_string l =
  try int_of_string l with
  | Failure _ -> perform (Conversion_failure l)

where the second definition of int_of_string calls the first one which raises the first version of the Conversion_failure effect.

You can check that replacing the definition of int_of_string to avoid this shadowing:

let int_of_string l =
  try Stdlib.int_of_string l with
  | Failure _ -> perform (Conversion_failure l)

fixes this issue.

emillon commented 11 months ago

Oh, great. I should have started by testing on the plain ocaml toplevel to see if there was an issue. Thanks a lot for debugging this.

andrewray commented 11 months ago

Ahh - that makes sense. Thankyou for the explanation.