packages feed

bluefin-postgresql (empty) → 0.1.0.0

raw patch · 7 files changed

+729/−0 lines, 7 filesdep +basedep +bluefindep +postgresql-simple

Dependencies added: base, bluefin, postgresql-simple, unliftio-pool

Files

+ CHANGELOG.md view
@@ -0,0 +1,20 @@+# Changelog++All notable changes to `bluefin-postgresql` will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),+and this project adheres to [Haskell Package Versioning Policy](https://pvp.haskell.org).++## [Unreleased]++## [0.1.0.0] - 27.02.2026++### Added++- First edition of the package, ready for feedback.+- 100% documentation coverage.+- Reasonably detailed READMEs.+- CI that builds and tests the packages for each version of GHC in the `tested-with` field.++[unreleased]: https://github.com/fpringle/bluefin-postgresql/compare/bluefin-postgresql-0.1.0.0...HEAD+[0.1.0.0]: https://github.com/fpringle/bluefin-postgresql/releases/tag/bluefin-postgresql-0.1.0.0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2025, Frederick Pringle++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Frederick Pringle nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,105 @@+# bluefin-postgresql++This package provides a `bluefin` effect for [postgresql-simple](https://hackage.haskell.org/package/postgresql-simple)'s `Connection` type.++It defines a dynamic effect to allow effectful functions to use a `Connection`, without worrying about where that `Connection` comes from.++For a higher-level effect library using [Opaleye](https://hackage.haskell.org/package/opaleye), see [bluefin-opaleye](https://github.com/fpringle/bluefin-postgresql/blob/main/bluefin-opaleye#readme).++## Effectful functions++In the `WithConnection` effect we can always request a `Connection` and use it as we normally+would:++```haskell+import Bluefin.PostgreSQL as BP+import qualified Database.PostgreSQL.Simple as PSQL++insertAndList ::+  (e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  Eff es [User]+insertAndList wc ioe = BP.withConnection wc $ \conn -> do+  effIO ioe $ PSQL.execute conn "insert into users (first_name) values (?)" ["Nuala"]+  effIO ioe $ PSQL.query conn "select * from users where first_name in ?" $ PSQL.Only $ PSQL.In ["Anna", "Boris", "Carla"]+```++In fact, for convenience we also define lifted versions of all of the query/execute+functions from `postgresql-simple`, so we can completely forget about `Connection` and rewrite the above to:++```haskell++import Bluefin.PostgreSQL++insertAndList ::+  (e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  Eff es [User]+insertAndList wc ioe = do+  BP.execute wc ioe "insert into users (first_name) values (?)" ["Nuala"]+  BP.query wc ioe "select * from users where first_name in ?" $ PSQL.Only $ PSQL.In ["Anna", "Boris", "Carla"]+```++The same goes for other functions:++```haskell+-- use a transaction+insertAndListCarefully ::+  (e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  Eff es [User]+insertAndListCarefully wc ioe = BP.withTransaction wc ioe $ insertAndList wc ioe++-- stream + fold over results (in Eff)+countUsersIneffeciently ::+  (e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  Eff es Int+countUsersIneffeciently wc ioe =+  BP.fold_ wc ioe "select * from users" 0 $ \acc (row :: User) -> do+    effIO ioe . putStrLn $ "User: " <> show row+    pure $ acc + 1+```++## Interpreters++The simplest way of running the `WithConnection` effect is by just providing a `Connection`, which we can get in the normal ways:++```haskell+import Bluefin.PostgreSQL as BP+import qualified Database.PostgreSQL.Simple as PSQL++usingConnection :: IO ()+usingConnection =+  runEff $ \ioe ->+    bracket (effIO ioe $ PSQL.connectPostgreSQL "") (effIO ioe . PSQL.close) $ \conn ->+      BP.runWithConnection conn $ \wc -> insertAndListCarefully wc ioe >>= effIO ioe . print++usingConnectInfo :: IO ()+usingConnectInfo =+  runEff $ \ioe ->+    BP.runWithConnectInfo ioe PSQL.defaultConnectInfo $ \wc ->+      insertAndListCarefully wc ioe >>= effIO ioe . print+```++Alternatively, we can use a connection pool (from [resource-pool](https://hackage.haskell.org/package/resource-pool)+and [unliftio-pool](https://hackage.haskell.org/package/unliftio-pool)), which is much better suited to+long-running processes like servers.++```haskell+import Bluefin.PostgreSQL as BP+import qualified Database.PostgreSQL.Simple as PSQL+import qualified UnliftIO.Pool as P++usingConnectionPool :: IO ()+usingConnectionPool = do+  poolCfg <- P.mkDefaultPoolConfig (PSQL.connectPostgreSQL "") PSQL.close 5.0 10+  pool <- P.newPool poolCfg+  runEff $ \ioe ->+    BP.runWithConnectionPool ioe pool $ \wc ->+      insertAndListCarefully wc ioe >>= effIO ioe . print+```
+ bluefin-postgresql.cabal view
@@ -0,0 +1,79 @@+cabal-version:      3.0+name:               bluefin-postgresql+version:            0.1.0.0+synopsis:+  bluefin support for mid-level PostgreSQL operations.+description:+  See the README for an overview, or the documentation in 'Bluefin.PostgreSQL'.+license:            BSD-3-Clause+license-file:       LICENSE+author:             Frederick Pringle+maintainer:         frederick.pringle@fpringle.com+copyright:          Copyright(c) Frederick Pringle 2025+homepage:           https://github.com/fpringle/bluefin-postgresql+build-type:         Simple+category:           Database+extra-doc-files:    README.md+                    CHANGELOG.md++tested-with:+    GHC == 8.10.7+  , GHC == 9.0.2+  , GHC == 9.2.4+  , GHC == 9.2.8+  , GHC == 9.4.2+  , GHC == 9.4.5+  , GHC == 9.6.1+  , GHC == 9.6.7+  , GHC == 9.8.2+  , GHC == 9.10.2++flag enable-pool+  description: Enable support for connection pools using unliftio-pool.+               You can disable this for a lighter dependency footprint if+               you don't need support for connection pools.+  default: True+  manual: False++common warnings+  ghc-options: -Wall -Wno-unused-do-bind -Wunused-packages++  if flag(enable-pool)+    cpp-options: -DPOOL++common deps+  build-depends:+    , base >= 4 && < 5+    , bluefin >= 0.0.11 && < 0.0.18+    , postgresql-simple >= 0.7 && < 0.8++  if flag(enable-pool)+    build-depends:+      , unliftio-pool >= 0.4.1 && < 0.5++common extensions+  default-extensions:+    DataKinds+    FlexibleContexts+    GADTs+    LambdaCase+    RankNTypes+    ScopedTypeVariables+    TypeApplications+    TypeFamilies+    TypeOperators++library+  import:+      warnings+    , deps+    , extensions+  exposed-modules:+      Bluefin.PostgreSQL+      Bluefin.PostgreSQL.Connection+  if flag(enable-pool)+    exposed-modules:+      Bluefin.PostgreSQL.Connection.Pool+  -- other-extensions:+  hs-source-dirs:   src+  default-language: Haskell2010
+ src/Bluefin/PostgreSQL.hs view
@@ -0,0 +1,384 @@+{-# LANGUAGE CPP #-}++module Bluefin.PostgreSQL+  ( -- * Effect+    WithConnection+  , withConnection++    -- ** Interpreters+  , runWithConnection++#if POOL+  , runWithConnectionPool+#endif++    -- * Lifted versions of functions from Database.PostgreSQL.Simple++    -- ** Queries that return results+  , query+  , query_+  , queryWith+  , queryWith_++    -- ** Statements that do not return results+  , execute+  , execute_+  , executeMany++    -- ** Transaction handling+  , withTransaction+  , withSavepoint+  , begin+  , commit+  , rollback++    -- ** Queries that stream results+  , fold+  , foldWithOptions+  , fold_+  , foldWithOptions_+  , forEach+  , forEach_+  , returning+  , foldWith+  , foldWithOptionsAndParser+  , foldWith_+  , foldWithOptionsAndParser_+  , forEachWith+  , forEachWith_+  , returningWith+  )+where++import Data.Int (Int64)+import qualified Database.PostgreSQL.Simple as PSQL+import qualified Database.PostgreSQL.Simple.FromRow as PSQL+import Bluefin.Eff+import Bluefin.IO+import Bluefin.PostgreSQL.Connection as Conn+import GHC.Stack+#if POOL+import Bluefin.PostgreSQL.Connection.Pool as Pool+#endif++-- | Lifted 'PSQL.query'.+query ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow q, PSQL.FromRow r) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  q ->+  Eff es [r]+query wc ioe q row = withConnection wc $ \conn -> effIO ioe (PSQL.query conn q row)+++-- | Lifted 'PSQL.query_'.+query_ ::+  (HasCallStack, e :> es, e1 :> es, PSQL.FromRow r) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  Eff es [r]+query_ wc ioe row = withConnection wc $ \conn -> effIO ioe (PSQL.query_ conn row)++-- | Lifted 'PSQL.queryWith'.+queryWith ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow q) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.RowParser r ->+  PSQL.Query ->+  q ->+  Eff es [r]+queryWith wc ioe parser q row =+  withConnection wc $ \conn -> effIO ioe (PSQL.queryWith parser conn q row)++-- | Lifted 'PSQL.queryWith_'.+queryWith_ ::+  (HasCallStack, e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.RowParser r ->+  PSQL.Query ->+  Eff es [r]+queryWith_ wc ioe parser row =+  withConnection wc $ \conn -> effIO ioe (PSQL.queryWith_ parser conn row)++-- | Lifted 'PSQL.execute'.+execute ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow q) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  q ->+  Eff es Int64+execute wc ioe q row = withConnection wc $ \conn -> effIO ioe (PSQL.execute conn q row)++-- | Lifted 'PSQL.execute_'.+execute_ ::+  (HasCallStack, e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  Eff es Int64+execute_ wc ioe row = withConnection wc $ \conn -> effIO ioe (PSQL.execute_ conn row)++-- | Lifted 'PSQL.executeMany'.+executeMany ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow q) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  [q] ->+  Eff es Int64+executeMany wc ioe q rows = withConnection wc $ \conn -> effIO ioe (PSQL.executeMany conn q rows)++-- | Lifted 'PSQL.withTransaction'.+withTransaction ::+  (HasCallStack, e :> es, e1 :> es) => +  WithConnection e ->+  IOE e1 ->+  Eff es a -> +  Eff es a+withTransaction wc ioe f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.withTransaction conn (unlift f)++-- | Lifted 'PSQL.withSavepoint'.+withSavepoint ::+  (HasCallStack, e :> es, e1 :> es) => +  WithConnection e ->+  IOE e1 ->+  Eff es a -> Eff es a+withSavepoint wc ioe f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.withSavepoint conn (unlift f)++-- | Lifted 'PSQL.begin'.+begin ::+  (HasCallStack, e :> es, e1 :> es) => +  WithConnection e ->+  IOE e1 ->+  Eff es ()+begin wc ioe = withConnection wc $ effIO ioe . PSQL.begin++-- | Lifted 'PSQL.commit'.+commit ::+  (HasCallStack, e :> es, e1 :> es) => +  WithConnection e ->+  IOE e1 ->+  Eff es ()+commit wc ioe = withConnection wc $ effIO ioe . PSQL.commit++-- | Lifted 'PSQL.rollback'.+rollback ::+  (HasCallStack, e :> es, e1 :> es) => +  WithConnection e ->+  IOE e1 ->+  Eff es ()+rollback wc ioe = withConnection wc $ effIO ioe . PSQL.rollback++(...) :: (a -> b) -> (t1 -> t2 -> a) -> t1 -> t2 -> b+unlift ... f = \a' row -> unlift $ f a' row++unliftWithConn ::+  (HasCallStack, e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  (PSQL.Connection -> (forall b. Eff es b -> IO b) -> IO a) ->+  Eff es a+unliftWithConn wc ioe f =+  withConnection wc $ \conn ->+    withEffToIO_ ioe $ \unlift ->+      f conn unlift+{-# INLINE unliftWithConn #-}++-- | Lifted 'PSQL.fold'.+fold ::+  (HasCallStack, e :> es, e1 :> es, PSQL.FromRow row, PSQL.ToRow params) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  params ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+fold wc ioe q params a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.fold conn q params a (unlift ... f)++-- | Lifted 'PSQL.foldWithOptions'.+foldWithOptions ::+  (HasCallStack, e :> es, e1 :> es, PSQL.FromRow row, PSQL.ToRow params) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.FoldOptions ->+  PSQL.Query ->+  params ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+foldWithOptions wc ioe opts q params a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.foldWithOptions opts conn q params a (unlift ... f)++-- | Lifted 'PSQL.fold_'.+fold_ ::+  (HasCallStack, e :> es, e1 :> es, PSQL.FromRow row) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+fold_ wc ioe q a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.fold_ conn q a (unlift ... f)++-- | Lifted 'PSQL.foldWithOptions_'.+foldWithOptions_ ::+  (HasCallStack, e :> es, e1 :> es, PSQL.FromRow row) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.FoldOptions ->+  PSQL.Query ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+foldWithOptions_ wc ioe opts q a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.foldWithOptions_ opts conn q a (unlift ... f)++-- | Lifted 'PSQL.forEach'.+forEach ::+  (HasCallStack, e :> es, e1 :> es, PSQL.FromRow r, PSQL.ToRow q) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  q ->+  (r -> Eff es ()) ->+  Eff es ()+forEach wc ioe q row forR =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.forEach conn q row (unlift . forR)++-- | Lifted 'PSQL.forEach_'.+forEach_ ::+  (HasCallStack, e :> es, e1 :> es, PSQL.FromRow r) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  (r -> Eff es ()) ->+  Eff es ()+forEach_ wc ioe q forR =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.forEach_ conn q (unlift . forR)++-- | Lifted 'PSQL.returning'.+returning ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow q, PSQL.FromRow r) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.Query ->+  [q] ->+  Eff es [r]+returning wc ioe q rows = withConnection wc $ \conn -> effIO ioe $ PSQL.returning conn q rows++-- | Lifted 'PSQL.foldWith'.+foldWith ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow params) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.RowParser row ->+  PSQL.Query ->+  params ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+foldWith wc ioe parser q params a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.foldWith parser conn q params a (unlift ... f)++-- | Lifted 'PSQL.foldWithOptionsAndParser'.+foldWithOptionsAndParser ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow params) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.FoldOptions ->+  PSQL.RowParser row ->+  PSQL.Query ->+  params ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+foldWithOptionsAndParser wc ioe opts parser q params a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.foldWithOptionsAndParser opts parser conn q params a (unlift ... f)++-- | Lifted 'PSQL.foldWith_'.+foldWith_ ::+  (HasCallStack, e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.RowParser row ->+  PSQL.Query ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+foldWith_ wc ioe parser q a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.foldWith_ parser conn q a (unlift ... f)++-- | Lifted 'PSQL.foldWithOptionsAndParser_'.+foldWithOptionsAndParser_ ::+  (HasCallStack, e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.FoldOptions ->+  PSQL.RowParser row ->+  PSQL.Query ->+  a ->+  (a -> row -> Eff es a) ->+  Eff es a+foldWithOptionsAndParser_ wc ioe opts parser q a f =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.foldWithOptionsAndParser_ opts parser conn q a (unlift ... f)++-- | Lifted 'PSQL.forEachWith'.+forEachWith ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow q) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.RowParser r ->+  PSQL.Query ->+  q ->+  (r -> Eff es ()) ->+  Eff es ()+forEachWith wc ioe parser q row forR =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.forEachWith parser conn q row (unlift . forR)++-- | Lifted 'PSQL.forEachWith_'.+forEachWith_ ::+  (HasCallStack, e :> es, e1 :> es) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.RowParser r ->+  PSQL.Query ->+  (r -> Eff es ()) ->+  Eff es ()+forEachWith_ wc ioe parser row forR =+  unliftWithConn wc ioe $ \conn unlift ->+    PSQL.forEachWith_ parser conn row (unlift . forR)++-- | Lifted 'PSQL.returningWith'.+returningWith ::+  (HasCallStack, e :> es, e1 :> es, PSQL.ToRow q) =>+  WithConnection e ->+  IOE e1 ->+  PSQL.RowParser r ->+  PSQL.Query ->+  [q] ->+  Eff es [r]+returningWith wc ioe parser q rows =+  withConnection wc $ \conn -> effIO ioe $ PSQL.returningWith parser conn q rows
+ src/Bluefin/PostgreSQL/Connection.hs view
@@ -0,0 +1,74 @@+module Bluefin.PostgreSQL.Connection+  ( -- * Effect+    WithConnection (..)+  , withConnection++    -- * Interpret with a single Connection+  , runWithConnection+  , runWithConnectInfo+  )+where++import Bluefin.Compound+import Bluefin.Eff+import Bluefin.IO+import qualified Database.PostgreSQL.Simple as PSQL+import GHC.Stack++{- | A dynamic effect that lets us use a database 'PSQL.Connection',+without specifying __how__ that connection is supplied.++For example, we might want to just provide a connection and let the interpreter+use that for the whole procedure (see 'Bluefin.PostgreSQL.Connection.runWithConnection').++Or, we might want to create a connection pool which provides connections when+the interpreter asks for them (see "Bluefin.PostgreSQL.Connection.Pool").+-}+newtype WithConnection (e :: Effects) = MkWithConnection+  { withConnectionImpl :: forall e' a. (PSQL.Connection -> Eff e' a) -> Eff (e' :& e) a+  -- ^ Use a 'PSQL.Connection' provided by an interpreter.+  }++instance Handle WithConnection where+  mapHandle h =+    MkWithConnection+      { withConnectionImpl = useImplUnder . withConnectionImpl h+      }++-- | Use a 'PSQL.Connection' provided by an interpreter.+withConnection :: (e :> es) => WithConnection e -> (PSQL.Connection -> Eff es a) -> Eff es a+withConnection w f = makeOp (withConnectionImpl (mapHandle w) f)++{- | Run a t'WithConnection' effect by simply supplying a 'PSQL.Connection'.+The connection will be kept alive for the whole duration of the procedure,+which might not be want you want for long-running processes. If so, see+"Bluefin.PostgreSQL.Connection.Pool".+-}+runWithConnection ::+  PSQL.Connection ->+  (forall e. WithConnection e -> Eff (e :& es) a) ->+  Eff es a+runWithConnection conn k =+  useImplIn+    k+    MkWithConnection+      { withConnectionImpl = \f -> useImpl (f conn)+      }++{- | Run a t'WithConnection' effect using a 'PSQL.ConnectInfo'.+The 'PSQL.ConnectInfo' will be used to create a 'PSQL.Connection' which will be+kept alive for the whole duration of the procedure, which might not be want you want+for long-running processes. If so, see "Bluefin.PostgreSQL.Connection.Pool".++'PSQL.withConnect' will handle opening and closing the 'PSQL.Connection'.+-}+runWithConnectInfo ::+  (HasCallStack, e1 :> es) =>+  IOE e1 ->+  PSQL.ConnectInfo ->+  (forall e. WithConnection e -> Eff (e :& es) a) ->+  Eff es a+runWithConnectInfo ioe connInfo k =+  withEffToIO_ ioe $ \unlift ->+    PSQL.withConnect connInfo $ \conn ->+      unlift $ runWithConnection conn k
+ src/Bluefin/PostgreSQL/Connection/Pool.hs view
@@ -0,0 +1,37 @@+module Bluefin.PostgreSQL.Connection.Pool+  ( -- * Interpret with a Connection pool+    runWithConnectionPool++    -- * Re-export+  , module Pool+  )+where++import Bluefin.Compound+import Bluefin.Eff+import Bluefin.IO+import Bluefin.PostgreSQL.Connection+import qualified Database.PostgreSQL.Simple as PSQL+import GHC.Stack+import UnliftIO.Pool as Pool++{- | Rather than keeping one connection alive and re-using it for the whole+process, we might want to create a 'Pool' of connections and only "ask" for+one when we need it. This function uses "UnliftIO.Pool" to do just that.+-}+runWithConnectionPool ::+  (HasCallStack, e1 :> es) =>+  IOE e1 ->+  Pool.Pool PSQL.Connection ->+  (forall e. WithConnection e -> Eff (e :& es) a) ->+  Eff es a+runWithConnectionPool ioe pool k =+  useImplIn+    k+    MkWithConnection+      { withConnectionImpl = \f ->+          runEffReader ioe $+            Pool.withResource pool $+              \conn -> effReader $+                \_ -> useImpl (f conn)+      }