-
Notifications
You must be signed in to change notification settings - Fork 2
Description
This PR rotted a lot and should probably be closed: #28
Anyway I would like to instead open an issue to discuss the core problem it was attempting to address.
This library exposes 2 types of errors:
- WithNamedError is used for errors that come from implementation of this library (named argument not found)
- IO exceptions that happen within postgresql-simple
My claim is that one never wants to handle the 1 (it's implementation error not legitimate runtime error in most cases). But almost always wants to handle some subset of 2.
The problem
Putting WithNamedError
thing aside lets clarify why IO exceptions matter.
Imagine a table with UNIQUE
constrained. For obvious reasons when inserting data to the table there is an expected error that could happen - violation of unique constrained. This error most likely needs to be clearly communicated to caller - for instance the specific 400 response with information that name is taken
in some form should be rendered by HTTP server.
The solution that this library encourages is to write 2 queries (why is explained bellow). One to check if given unique column doesn't already exist and then performing the insert. This solution is of course incorrect. In presence of concurrency some other thread could create conflicting entry in between the select and insert.
The correct solution is to perform insert and catch unique constrained violation. So what does it take to do this?
With postgresql-simple one works with IO
so catching this means catching exception in IO monad. However with postgresql-simple-named
this IO
is lifted to LiftIO m => m
. To catch the exception one needs to use construct that could catch it in the m
(lifted IO context). For instance using Control.Exception.Lifted which is not part base or implement similar exception handler to this one. I claim that this is no good because good API should make correct implementation simpler not harder.
Proposed solutions
There are multiple potential solutions. I have some ideas myself but first I would like to see what others think.
Currently there is an issue for one possible approach #27