I have been using Lwt’s Syntax extensions: let* and let+ for awhile, but now I noticed that thrown exceptions lose valuable information compared to let%lwt.
For example with code using let* () = ... I get (which isn’t very useful):
ASSERT err
FAIL err
Raised at file "src/alcotest-engine/test.ml", line 143, characters 20-48
Called from file "src/core/lwt.ml", line 1864, characters 23-26
but with let%lwt () = ... I get:
ASSERT err
FAIL err
Raised at file "src/alcotest-engine/test.ml", line 143, characters 20-48
Called from file "src/core/lwt.ml", line 1927, characters 23-26
Re-raised at file "src/my_tests/my_tests.ml", line 41, characters 16-129
it shows exactly where it failed.
How can I fix this, so that the syntax extensions also include where the exception was thrown?
I don’t think you’ll able to replicate that with a (let*) because the backtrace would point at the point of definition of (let*), and not at the call site.
You can figure out the call site location by walking up the call stack (see this code which does exactly that). So you could track the location of every bind. However you couldn’t inject such a location in a regular backtrace, so the backtrace would have to be manually maintained by the Lwt module.
For some errors, it might make sense to include the location of the call site in the error payload. This is similar to how the Assert_failure includes location information in its constructor parameters.
It’s not an ideal solution because you might accidentally bubble up location information into error messages seen by user. But if you are careful you can strip that when you pretty-print the errors in production mode. It’s also less than ideal because you’re shouldering the burden of location tracking yourself.