-
| We're currently using Here's my translation of our hasql effect: import Effectful import Effectful.Dispatch.Dynamic import Effectful.Error.Static import Effectful.Reader.Static import Hasql.Pool (Pool, UsageError, use) import Hasql.Session (Session) data Hasql :: Effect where RunSession :: Session a -> Hasql m a type instance DispatchOf Hasql = Dynamic runSession :: (Hasql :> es) => Session a -> Eff es a runSession = send . RunSession runHasqlIO :: forall es a. ( IOE :> es, Reader Pool :> es, Error UsageError :> es ) => Eff (Hasql : es) a -> Eff es a runHasqlIO = interpret $ \_ -> \case RunSession session -> do pool <- ask @Pool r <- liftIO $ use pool session either throwError pure rHere's an example demonstrating its usage: data User userDecoder :: D.Row User userDecoder = undefined userEncoder :: E.Params User userEncoder = undefined insertUser_ :: Statement User Int32 insertUser_ = undefined findUserById_ :: Statement Int32 (Maybe User) findUserById_ = undefined findUserById :: Int32 -> Session (Maybe User) findUserById = undefined insertUser :: User -> Session Int32 insertUser = undefined createUser :: (Hasql :> es) => User -> Eff es Int32 createUser user = undefined findUser :: (Hasql :> es) => Int32 -> Eff es (Maybe User) findUser userId = undefined newUser :: User newUser = undefined exampleApp :: (Hasql :> es) => Eff es (Maybe User) exampleApp = do userId <- createUser newUser findUser userIdGiven the setup above, I'd like to ask if it's advisable to encapsulate the Currently running the effect would look something like that: run :: Pool -> Eff [Hasql, Reader Pool, Error UsageError, IOE] a -> IO (Either (CallStack, UsageError) a) run pool = runEff . runError @UsageError . runReader pool . runHasqlIO runWithErrorHandler :: Pool -> (CallStack -> UsageError -> Eff '[IOE] a) -> Eff [Hasql, Reader Pool, Error UsageError, IOE] a -> IO a runWithErrorHandler pool errHandler = runEff . runErrorWith @UsageError errHandler . runReader pool . runHasqlIO |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
| Looks good to me 👍 You might want to include
I'd not include runHasqlIO :: forall es a. ( IOE :> es, Error UsageError :> es ) => Eff (Hasql : es) a -> Eff es a runHasqlIO pool = interpret $ \_ -> \case RunSession session -> do r <- liftIO $ use pool session either throwError pure rsince I don't think you'll use As for the error handling, you can have a look at #149, it has some pondering that might be helpful, especially this comment. If you do this, then it would be import Effectful import Effectful.Dispatch.Dynamic import Effectful.Error.Static import Effectful.Reader.Static import Hasql.Pool (Pool, UsageError, use) import Hasql.Session (Session) data Hasql :: Effect where RunSession :: Error UsageError :> es => Session a -> Hasql (Eff es) a type instance DispatchOf Hasql = Dynamic runSession :: (HasCallStack, Error UsageError :> es, Hasql :> es) => Session a -> Eff es a runSession = send . RunSession runHasqlIO :: forall es a. ( IOE :> es, ) => Pool -> Eff (Hasql : es) a -> Eff es a runHasqlIO pool = interpret $ \env -> \case RunSession session -> do r <- liftIO $ use pool session localSeqUnlift env $ \unlift -> unlift $ do either throwError pure rThe difference is that now you don't need the |
Beta Was this translation helpful? Give feedback.
Looks good to me 👍 You might want to include
HasCallStackconstraint in the signature ofrunSessionto be able to get accurate stack traces in case of errors.I'd not include
Readerat all and just pass pool as a parameter torunHasqlIO:since I don't think you'll use
localfrom outside to modify the pool?A…