Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose lower level interface for executing statements #121

Open
jfischoff opened this issue Feb 9, 2020 · 3 comments
Open

Expose lower level interface for executing statements #121

jfischoff opened this issue Feb 9, 2020 · 3 comments

Comments

@jfischoff
Copy link
Contributor

I would like to make my own version of the Session monad that has additional type class instances and other small changes.

To facilitate alternative Session monads it would be nice if hasql exposed lower level functions for running statements.

For instance if we had:

statementConn :: Connection -> params -> Statement params result -> IO (Either QueryError result)

and statement could be defined in terms of it:

statement x y = Session $ ReaderT $ \conn -> ExceptT $ statementConn x y

Something to consider is removing the Session monad entirely from hasql and putting it in it's own package like hasql-session and only having an explicit connection passing API in IO in hasql.

@jfischoff jfischoff changed the title Exposes lower level interface for executing statements Expose lower level interface for executing statements Feb 9, 2020
@nikita-volkov
Copy link
Owner

Sounds like something to give a thought to.

What exactly makes you need this?

@jfischoff
Copy link
Contributor Author

The main reason currently is to help me write tests. However I think as a general design rule it is best to provide the low level features outside of monad to give users more flexibility so they can create their own monad.

In my tests I like to use transaction monad that has MonadThrow and MonadCatch instances. This allows me to have assertions inside the monad context vs having to run the context and then make the assertions.

You can compare the styles here:

MonadThrow way: https://github.com/jfischoff/postgresql-queue/blob/master/test/Database/PostgreSQL/Simple/QueueSpec.hs#L103-L112

The style Session is pushing me towards: https://github.com/jfischoff/hasql-queue/blob/master/test/Database/Hasql/QueueSpec.hs#L111-L121

The first style to me is much easier to write and read.

@nikita-volkov
Copy link
Owner

If we were to provide low-level features outside of abstractions, then this library would turn to be just a reexport of libpq. What I mean is that the sole point of this project is about isolating reusable abstractions, thus relieving the user from the head-aches of lower-level fuss.

I believe that downgrading the API to a lower-level of abstraction should only happen when the provided abstraction can be proven wrong or at least not useful. So far yours is the first request in the whole history of the project on that matter. I interpret that as a proof that users don't find that abstraction useless or wrong. In fact, Session is the abstraction from which Hasql was conceived. Back in the day that was the pattern that I've discovered in how I was using "postgresql-simple" everywhere. To this day I still am to find anything wrong with that abstraction.

If we were to extend the API with alternative ways of doing the same thing, then that would confuse the users and move the project backwards in terms of promoting a common usage style.

Besides Session in the libpq-free developments of Hasql, I've also discovered an abstraction amidst Session and Statement, codenamed Batch. It abstracts over execution of statements in batches with applicative functor (See http://hackage.haskell.org/package/hasql-0.20.1/docs/Hasql-Batch.html). Removing Session would raise questions to having that abstraction as well, yet I find myself amazed by how well that stack works together.

Finally, session does not limit you from achieving what you want in any way. Getting to your desired level of abstraction is as simple as the following:

runStatementInIO :: Connection -> params -> Statement params result -> IO (Either QueryError a)
runStatementInIO connection params statement =
  Session.run (Session.statement params statement) connection

Just extend your test-suite with whatever helper function that suffices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants