Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Dream with effects #297

Open
ul opened this issue Aug 31, 2023 · 1 comment
Open

Using Dream with effects #297

ul opened this issue Aug 31, 2023 · 1 comment

Comments

@ul
Copy link

ul commented Aug 31, 2023

Consider the following snippet (it's not a strictly minimal example; I hope that's fine):

open Dream
open Effect
open Effect.Deep
open Ppx_yojson_conv_lib.Yojson_conv.Primitives

type user_object = {
  email : string;
  token: string;
  username: string;
  bio: string;
  image: string option;
} [@@deriving yojson]

type login_user_object = {
  email : string;
  password: string;
} [@@deriving yojson]

type login_object = {
  user: login_user_object;
} [@@deriving yojson]

type _ Effect.t += User_login : login_object -> user_object Effect.t

let user_login x = User_login x |> perform

let with_handlers f x =
  try_with f x
    { effc = fun (type a) (eff: a t) ->
          match eff with
          | User_login _ -> Some (fun (k: (a, _) continuation) ->
              continue k { email = "test"; token = "test"; username = "test"; bio = "test"; image = None })
          | _ -> None }

let main() =
  run ~error_handler: debug_error_handler
  @@ logger
  @@ router [
    get "/" (fun _ -> html "Hello, world!");
    post "/api/users/login" @@
    (fun request ->
       let%lwt body = Dream.body request in
       body
       |> Yojson.Safe.from_string
       |> login_object_of_yojson
       |> user_login
       |> yojson_of_user_object
       |> Yojson.Safe.to_string
       |> json);
  ]

let () = with_handlers main ()

Making a POST /api/users/login request with a valid payload fails because the effect appears to be unhandled. As well as the following variation:

(* ... snip ... *)
let () =
  run ~error_handler: debug_error_handler
  @@ logger
  @@ router [
    get "/" (fun _ -> html "Hello, world!");
    post "/api/users/login" @@
    with_handlers (fun request ->
       let%lwt body = Dream.body request in
       body
       |> Yojson.Safe.from_string
       |> login_object_of_yojson
       |> user_login
       |> yojson_of_user_object
       |> Yojson.Safe.to_string
       |> json);
  ]

Lwt seems at fault here, as moving with_handlers inside let%lwt or not parsing the request body at all works as expected with the second variation. Are there any tips on how to use Dream with some top-level effect handlers? Either by installing them in a way that works with Lwt or duplicating them more ergonomically than just carefully spotting all use of async API from Dream and manually inserting effect handler inside the promise handlers.

@aantron
Copy link
Owner

aantron commented Nov 12, 2023

I minimized this example (please do so! :)) to confirm that this is indeed an Lwt issue:

type _ Effect.t += E : unit Effect.t

let () =
  Effect.Deep.try_with
    begin fun () ->
      Lwt_main.run begin
        Lwt.bind (Lwt_unix.sleep 1.) @@ fun () ->
        Effect.perform E;
        assert false
      end
    end
    ()
    {
      effc = fun (type a) (e : a Effect.t) ->
        match e with
        | E ->
          Option.some @@ fun (k : (a, _) Effect.Deep.continuation) ->
            prerr_endline "handling E";
            Effect.Deep.continue k ()
        | _ -> None
    }

I've opened ocsigen/lwt#1003 to ask about it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants