# Combining let syntax

**URL:** https://discuss.ocaml.org/t/combining-let-syntax/16986
**Category:** Learning
**Tags:** ppx
**Created:** [July 17, 2025, 8:35am UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986 "2025-07-17T08:35:10Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![vadymbiliuk](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/vadymbiliuk/32/6088_2.png) [@vadymbiliuk](https://discuss.ocaml.org/u/vadymbiliuk)
#### Post date: [July 17, 2025, 8:35am UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/1 "2025-07-17T08:35:10Z")

</div>

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:

```ocaml
  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:

```ocaml
  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
  ...

```

doesn’t work either.

I’m new to ocaml so thank you in advance!

---

<div class="post-metadata">

### Author: ![threepwood](https://avatars.discourse-cdn.com/v4/letter/t/8dc957/32.png) [@threepwood](https://discuss.ocaml.org/u/threepwood)
#### Post date: [July 17, 2025, 12:20pm UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/2 "2025-07-17T12:20:02Z")

</div>

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:

```auto
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.))

---

<div class="post-metadata">

### Author: ![Khady](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/khady/32/469_2.png) [@Khady](https://discuss.ocaml.org/u/Khady)
#### Post date: [July 17, 2025, 12:21pm UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/3 "2025-07-17T12:21:13Z")

</div>

I believe that you should be able to use `let%bind.Result` and `let%bind.Lwt_result`

> <https://github.com/janestreet/ppx_let/blob/master/CHANGES.md#git-version>

---

<div class="post-metadata">

### Author: ![vadymbiliuk](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/vadymbiliuk/32/6088_2.png) [@vadymbiliuk](https://discuss.ocaml.org/u/vadymbiliuk)
#### Post date: [July 17, 2025, 1:42pm UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/4 "2025-07-17T13:42:25Z")

</div>

> [@threepwood](#):
>
> 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.

Not sure what do you mean, because I’m getting env variable and then using that variable for getting connection in other expression

---

<div class="post-metadata">

### Author: ![vadymbiliuk](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/vadymbiliuk/32/6088_2.png) [@vadymbiliuk](https://discuss.ocaml.org/u/vadymbiliuk)
#### Post date: [July 17, 2025, 1:43pm UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/5 "2025-07-17T13:43:56Z")

</div>

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

---

<div class="post-metadata">

### Author: ![vadymbiliuk](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/vadymbiliuk/32/6088_2.png) [@vadymbiliuk](https://discuss.ocaml.org/u/vadymbiliuk)
#### Post date: [July 17, 2025, 1:59pm UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/6 "2025-07-17T13:59:47Z")

</div>

I think I won with that:

```ocaml
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

```

---

<div class="post-metadata">

### Author: ![EmileTrotignon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/emiletrotignon/32/5913_2.png) [@EmileTrotignon](https://discuss.ocaml.org/u/EmileTrotignon)
#### Post date: [July 17, 2025, 2:12pm UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/7 "2025-07-17T14:12:44Z")

</div>

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:

```ocaml
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.

---

<div class="post-metadata">

### Author: ![vadymbiliuk](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/vadymbiliuk/32/6088_2.png) [@vadymbiliuk](https://discuss.ocaml.org/u/vadymbiliuk)
#### Post date: [July 19, 2025, 11:02am UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/8 "2025-07-19T11:02:46Z")

</div>

Got it, i tried lifting env load but i did it in wrong way i guess. Thanks!  
Good point

---

<div class="post-metadata">

### Author: ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)
#### Post date: [July 21, 2025, 7:02am UTC](https://discuss.ocaml.org/t/combining-let-syntax/16986/9 "2025-07-21T07:02:02Z")

</div>

There’s a library for mixing result and lwt and lwt-result using binding operators: [tezos-lwt-result-stdlib 17.3 (latest) · OCaml Package](https://ocaml.org/p/tezos-lwt-result-stdlib/latest/doc/tezos-lwt-result-stdlib/Tezos_lwt_result_stdlib/Lwtreslib/Bare/Monad/Lwt_result_syntax/index.html)

You bind lwt-result expressions with `let*`, lwt-only expressions with `let*!`, and result-only expressions with `let*?`. So you example would be

```ocaml
  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](https://ocaml.org/p/tezos-lwt-result-stdlib/17.3/doc/tezos-lwt-result-stdlib/Tezos_lwt_result_stdlib/Lwtreslib/Bare/List/index.html#traversal-variants)

There’s a bunch more things in there, check the doc for more info.
