bluefin-postgresql
This package provides a bluefin effect for 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, see bluefin-opaleye.
Effectful functions
In the WithConnection effect we can always request a Connection and use it as we normally
would:
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:
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:
-- 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:
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
and unliftio-pool), which is much better suited to
long-running processes like servers.
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