aantron / dream

Tidy, feature-complete Web framework

Home Page:https://aantron.github.io/dream/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dream.sql / PostgreSQL / ppx_rapper: incompatible types

El-Profesor opened this issue · comments

Attempt to get Dream working with ppx_rapper but get the following type error:

Error: This expression has type
         unit ->
         (module Rapper_helper.CONNECTION) ->
         ((string * string) option, [> Caqti_error.call_or_retrieve ]) result
         Lwt.t
       but an expression was expected of type
         Caqti_lwt.connection -> 'a Lwt.t
       Type unit is not compatible with type
         Caqti_lwt.connection = (module Caqti_lwt.CONNECTION) 
       Hint: Did you forget to provide `()' as argument?

I'm relatively new to OCaml, I understand the error, but I couldn't get the following code (commented ppx_rapper version) to work.
Any idea about this ? Thanks in advance.

module type DB = Caqti_lwt.CONNECTION
module T = Caqti_type

(* (1) ----- Does not work (ppx_rapper version)
let list_buyers =
  [%rapper
    get_many
      {sql|
      SELECT @string{first_name}, @string{last_name}
      FROM buyer
      |sql}] *)

(* (2) ----- Works (Caqti version) *)
let list_buyers =
  let query =
    let open Caqti_request.Infix in
    (T.unit ->* T.(tup2 string string))
    "SELECT first_name, last_name FROM buyer" in
  fun (module Db : DB) ->
    let%lwt buyers_or_error = Db.collect_list query () in
    Caqti_lwt.or_fail buyers_or_error

let render buyers request =
  <html>
  <body>
%   buyers |> List.iter (fun (_id, buyer) ->
      <p><%s buyer %></p><% ); %>
    <form method="POST" action="/">
      <%s! Dream.csrf_tag request %>
      <input name="text" autofocus>
    </form>
  </body>
  </html>

let () =
  Dream.run
  @@ Dream.logger
  @@ Dream.sql_pool "postgresql://mentor:superMentor@localhost/dream_market"
  @@ Dream.sql_sessions
  @@ Dream.router [

    Dream.get "/" (fun request ->
      let%lwt buyers = Dream.sql request list_buyers in
      Dream.html (render buyers request));
  ]

I don't currently know ppx_rapper, and you didn't show which error the line is on, but it appears the error says list_buyers, generated by ppx_rapper, has an extra unit argument (generated by ppx_rapper). Could you do something like

let list_buyers connection =
  [%rapper
    get_many
      {sql|
      SELECT @string{first_name}, @string{last_name}
      FROM buyer
      |sql}]
    () connection

Thanks a lot for you reply @aantron.

« you didn't show which error the line is on »

File "bin/main.eml.ml", line 43, characters 41-52

which corresponds to:

let%lwt buyers = Dream.sql request list_buyers in`
Dream.html (render buyers request));

Sorry for missing this detail. Also, I thought ppx_rapper’s Dream integration was planned (Dream Wiki > Roadmap).

There is no integration with ppx_rapper planned. The roadmap says to study ppx_rapper -- I haven't done so yet :) Did my suggestion help?

Your suggestion only partially solved the type error, but it helped me realize that I need to learn OCaml more in depth before I can continue experimenting Dream.

But I hope to contribute to the Dream project in a while!

Thank you for your great work on Dream.

Note: If you're curious about it, here is the error message when applying your suggestion:

Error: This expression has type
         ((string * string) list, [> Caqti_error.call_or_retrieve ]) result
       but an expression was expected of type ('a * string) list

which corresponds to the second line below:

      let%lwt buyers = Dream.sql request list_buyers in
      Dream.html (render buyers request));

Absolutely I'm curious about it :) At this point, you need to pattern-match on the result of running the SQL query, because it might fail. Something like

let%lwt buyers = Dream.sql request list_buyers in
match buyers with
| Error _ ->
  assert false   (* But you should figure out how you actually want to handle this, if it is even possible. *)
| Ok buyers ->
  Dream.html (render buyers request)

There are other ways of writing it, such as using Result.get_ok (if the error is impossible) or match%lwt:

match%lwt Dream.sql request list_buyers with
| Error _ ->
  assert false   (* But you should figure out how you actually want to handle this, if it is even possible. *)
| Ok buyers ->
  Dream.html (render buyers request)

There are also probably various operators that propagate errors around until you actually want to handle them, but they would come from Caqti or ppx_rapper and I'm not familiar with them off the top of my head.

Both solutions work perfectly.

Now I have to understand why they work... So it’s time for me to study OCaml in depth.

I hope to be back here in a while.

Thank you very much for the explanations and for your help.