I have a case where I want to handle 2 different expressions with let%bind.
First is for result and second is for result Lwt.t. Is there a way how I can handle it?
If I open Result.Let_syntax it will fail on result Lwt.t. If I open Lwt_result.Let_syntax it will fail on result type.
Example:
let open Result.Let_syntax in
let%bind env = Gathering__Env.load () in
let%bind connection = Caqti_lwt.connect @@ Uri.of_string @@ env.database_url in
...
P.s doing something like:
let open Result.Let_syntax in
let%bind env = Gathering__Env.load () in
let open Lwt_result.Let_syntax in
let%bind connection = Caqti_lwt.connect @@ Uri.of_string @@ env.database_url in
...
I don’t know about let%bind but there is a solution for the name conflict if you use let-operators instead. The language has had them for some years and they do the same thing as let%bind (iirc, the main thing you lose relative to ppx_let is the match variant).
So what you do is define two operators yourself, for instance:
module My_syntax = struct
let (let*) = Result.bind
let (let$) = Lwt_result.bind
end
and then later you can do let open My_syntax in.
(The convention is to call all bind’s let*, but obviously this doesn’t work for you.)
HOWEVER you also have a problem with the types. If you use Result.bind, the expression after the let needs to be a result . But if you use Lwt_result.bind, you get a result Lwt.t. The minimal fix would be to wrap the final line in Ok (...), so that the whole expression is typed as a ((.., ...) result Lwt.t, ...) result, but I suspect this is not actually what you want. You need to think about the correct order of operations and I think perhaps it would be clearer if you do not use syntactic sugar here and write things out explicitly, as an exercise. And perhaps you do not actually want to define My_syntax because mixing these two is often a mistake. (What you possibly want to do is create a promise where you do the first result-bind, inside the promise, and then you call Caqti directly (since you are already inside the promise.))
Looks like it didn’t help. I believe it expects ppx syntax specifically for result Lwt.t. Looks like it cannot partially handle Lwt.t and give me back result etc
let _ =
let open Result.Let_syntax in
let%bind env = Gathering__Env.load () in
let connection_promise =
Lwt_main.run @@ Caqti_lwt.connect @@ Uri.of_string @@ env.database_url
in
let%bind connection = connection_promise in
That won’t help if you need to use Lwt in your whole program, and you will probably have to if you have a Db (most programs should only call Lwt_main.run once).
Try the following:
let _ =
let open Lwt_result.Let_syntax in
let%bind env = Lwt.return (Gathering__Env.load ()) in
let%bind connection =
Caqti_lwt.connect @@ Uri.of_string @@ env.database_url
in
Basically what you did is take your connection outside of the Lwt system. What I did is put the env inside it. In my case everything is of type _ Result.t Lwt.t, in yours everything is of type _ Result.t. My version should be more efficient and is more idiomatic.
The suggestion to not use syntactic sugar is to not use let%bind but Lwt_result.bind and Result.bind directly. Its going to make your code ugly, but you will probably get a better understanding of what is happening.
You bind lwt-result expressions with let*, lwt-only expressions with let*!, and result-only expressions with let*?. So you example would be
let open Lwt_result_syntax in
let*! env = Gathering__Env.load () in
let* connection = Caqti_lwt.connect @@ Uri.of_string @@ env.database_url in
...
Note that the same library has a few more things for dealing with lwt-result mixed codes. E.g., variants of List.iter for lwt-result iterators, lwt-only iterators, and result-only iterators: tezos-lwt-result-stdlib 17.3 · OCaml Package
There’s a bunch more things in there, check the doc for more info.