dylex / postgresql-typed

Haskell PostgreSQL library with compile-time type inference
http://hackage.haskell.org/package/postgresql-typed
Other
83 stars 12 forks source link

Pure version of functions #9

Closed joneshf closed 6 years ago

joneshf commented 6 years ago

Howdy again!

Many of the functions here end up throwing a PGError if something goes awry. However, some of these failures are just normal operation. E.g. failing a check constraint. In these cases, I want to handle them as part of normal program execution.

I can wrap calls to pgQuery in a try or whatever and end up with an IO (Either PGError [a]). But, I was curious if you were interested in providing a pure version where every function returned an Either PGError a instead of an a with an implicit PGError exception being possible. It looks like the line that needs to change is where throwIO is changed in pgReceive: https://github.com/dylex/postgresql-typed/blob/19b978262e77210ea5fdbf3d731bc00ba2a9c9f6/Database/PostgreSQL/Typed/Protocol.hs#L444

I recognize that this is a non-trivial change. But I am curious on your thoughts here.

dylex commented 6 years ago

My initial thought is that using try seems perfectly reasonable and should generally be safe and efficient. The easiest alternative would be to switch pgReceieve into an ExceptT monad and handle the errors that way, but since we already have to be in IO, it's not clear that there's much advantage to that.

The more significant thing there might be some reason for is more explicit error handling, trying to distinguish different types and sources of errors, but I didn't see an easy way to do this given the huge number of postgresql error codes.

Do you have a specific use case or reason to prefer the explicit interface? I certainly have no problem adding version of functions that produce Either or ExceptT or something just by adding a try, if that would help.

joneshf commented 6 years ago

If try is the suggested way to go, I'm fine with that. The onus needn't be on you to provide the pure versions, I can always write an adapter module locally that converts the functions I need so it's a bit less boilerplate. But if you want to add them, it would be greatly appreciated.

Yeah, I'm not sure handling all of the postgres errors is a useful thing.

In this particular case, unique_violation was something I wanted to handle. Due to the way pgTransaction was used in a larger context, try wasn't actually catching the exception when used outside of pgTransaction. If the try was run inside the pgTransaction, it was fine though and would catch the exception. I think it was something caused by typical exception handling in Haskell, but still a bit frustrating in the heat of the moment.

My thought is that, if pgQuery returned IO (Either PGError [a]) or ExceptT PGError IO [a], it would have bubbled up through the transaction no matter how IO was interleaved in another monad. Does that make sense?

joneshf commented 6 years ago

Sorry, by

But if you want to add them, it would be greatly appreciated.

I didn't mean you would have to do the work, I'd submit a PR for this.

dylex commented 6 years ago

Not sure if it's relevant, but I needed something similar which I did this way and seemed to work okay: https://github.com/dylex/databrary/blob/master/Databrary/Model/SQL.hs (I feel like I wrote a more generic version of that at some point but can't find it right now.)

joneshf commented 6 years ago

Cool! Thanks for the example.