effectful-opaleye (empty) → 0.1.0.0
raw patch · 5 files changed
+281/−0 lines, 5 filesdep +basedep +effectful-coredep +effectful-postgresql
Dependencies added: base, effectful-core, effectful-postgresql, effectful-th, opaleye, postgresql-simple, product-profunctors
Files
- CHANGELOG.md +20/−0
- LICENSE +30/−0
- README.md +49/−0
- effectful-opaleye.cabal +65/−0
- src/Effectful/Opaleye.hs +117/−0
+ CHANGELOG.md view
@@ -0,0 +1,20 @@+# Changelog++All notable changes to `effectful-opaleye` 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] - 22.07.2025++### 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/effectful-postgresql/compare/v0.1.0.0...HEAD+[0.1.0.0]: https://github.com/fpringle/effectful-postgresql/releases/tag/v0.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,49 @@+# effectful-opaleye++This package provides an `effectful` effect for [Opaleye](https://hackage.haskell.org/package/opaleye) operations.++It combines the very safe, high-level syntax of Opaleye, with the `WithConnection` abstraction of [effectful-postgresql](https://github.com/fpringle/effectful-postgresql/blob/main/effectful-postgresql#readme).++## Effectful functions++In the `Opaleye` effect we can perform the 4 main operations permitted by Opaleye: query, insert, delete, and update.++```haskell+{-# LANGUAGE Arrows #-}+import Control.Arrow+import Effectful.Opaleye as EO+import qualified Opaleye as O++insertAndList :: (EO.Opaleye :> es) => Eff es [User]+insertAndList = do+ EO.runInsert $ O.Insert userTable [User {firstName = "Nuala"}] O.rCount Nothing++ EO.runDelete $ O.Delete userTable isAdmin O.rCount++ EO.runUpdate $ O.Update userTable (\user -> user {updatedAt = O.now}) isAdmin O.rCount++ EO.runSelect $ proc () -> do+ user <- O.selectTable userTable -< ()+ O.restrict -< firstName user `O.in_` (O.toFields <$> ["Anna", "Boris", "Carla"])+ returnA -< user+```++## Interpreters++To run the `Opaleye` effect we can use the `WithConnection` effect from [effectful-postgresql](https://github.com/fpringle/effectful-postgresql/blob/main/effectful-postgresql#readme):++```haskell+import Effectful.PostgreSQL as EP+import Effectful.Opaleye as EO++doOpaleyeStuff :: (WithConnection :> es) => Eff es [User]+doOpaleyeStuff = EO.runOpaleyeWithConnection insertAndList+```++The `WithConnection` effect can then be dispatched using one of its [interpreters](https://github.com/fpringle/effectful-postgresql/blob/main/effectful-postgresql#interpreters).+Or, to skip that entirely, we can just use `runOpaleyeConnection`:++```haskell+doOpaleyeStuff :: PSQL.Connection -> Eff es [User]+doOpaleyeStuff conn = EO.runOpaleyeConnection conn insertAndList+```
+ effectful-opaleye.cabal view
@@ -0,0 +1,65 @@+cabal-version: 3.0+name: effectful-opaleye+version: 0.1.0.0+synopsis:+ effectful support for high-level PostgreSQL operations via Opaleye.+description:+ See the README for an overview, or the documentation in 'Effectful.Opaleye'.+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/effectful-postgresql+build-type: Simple+category: Database+extra-doc-files: README.md+ CHANGELOG.md++tested-with:+ GHC == 8.8.4+ , 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++common warnings+ ghc-options: -Wall -Wno-unused-do-bind -Wunused-packages++common deps+ build-depends:+ , base >= 4 && < 5+ , effectful-core >= 2.3 && < 2.6+ , effectful-th >= 1.0.0.1 && < 1.0.1+ , opaleye >= 0.9 && < 0.11+ , product-profunctors >= 0.9 && < 0.12+ , effectful-postgresql >= 0.1 && < 0.2+ , postgresql-simple >= 0.7 && < 0.8++common extensions+ default-extensions:+ DataKinds+ FlexibleContexts+ GADTs+ LambdaCase+ ScopedTypeVariables+ TypeApplications+ TypeFamilies+ TypeOperators++library+ import:+ warnings+ , deps+ , extensions+ exposed-modules:+ Effectful.Opaleye+ -- other-extensions:+ hs-source-dirs: src+ default-language: Haskell2010
+ src/Effectful/Opaleye.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE TemplateHaskell #-}++module Effectful.Opaleye+ ( -- * Effect+ Opaleye (..)++ -- * Effectful functions++ -- ** Select+ , runSelect+ , runSelectI+ , runSelectExplicit++ -- ** Select-fold+ , runSelectFold+ , runSelectFoldExplicit++ -- ** Insert+ , runInsert++ -- ** Delete+ , runDelete++ -- ** Update+ , runUpdate++ -- * Interpreters+ , runOpaleyeWithConnection+ , runOpaleyeConnection+ )+where++import Data.Profunctor.Product.Default+import qualified Database.PostgreSQL.Simple as PSQL+import Effectful+import Effectful.Dispatch.Dynamic+import qualified Effectful.PostgreSQL.Connection as Conn+import Effectful.TH+import qualified Opaleye as O+import qualified Opaleye.Internal.Inferrable as O++-- | A dynamic effect to perform @opaleye@ operations.+data Opaleye :: Effect where+ -- | Lifted 'O.RunSelectExplicit'.+ RunSelectExplicit :: O.FromFields fields haskells -> O.Select fields -> Opaleye m [haskells]+ -- | Lifted 'O.RunSelectFoldExplicit'.+ RunSelectFoldExplicit ::+ O.FromFields fields haskells ->+ O.Select fields ->+ b ->+ (b -> haskells -> m b) ->+ Opaleye m b+ -- | Lifted 'O.RunInsert'.+ RunInsert :: O.Insert haskells -> Opaleye m haskells+ -- | Lifted 'O.RunDelete'.+ RunDelete :: O.Delete haskells -> Opaleye m haskells+ -- | Lifted 'O.RunUpdate'.+ RunUpdate :: O.Update haskells -> Opaleye m haskells++makeEffect ''Opaleye++-- | Lifted 'O.runSelect'.+runSelect ::+ (HasCallStack, Opaleye :> es, Default O.FromFields fields haskells) =>+ O.Select fields ->+ Eff es [haskells]+runSelect = runSelectExplicit def++-- | Lifted 'O.runSelectFold'.+runSelectFold ::+ (HasCallStack, Opaleye :> es, Default O.FromFields fields haskells) =>+ O.Select fields ->+ b ->+ (b -> haskells -> Eff es b) ->+ Eff es b+runSelectFold = runSelectFoldExplicit def++-- | Lifted 'O.runSelectI'.+runSelectI ::+ (HasCallStack, Opaleye :> es, Default (O.Inferrable O.FromFields) fields haskells) =>+ O.Select fields ->+ Eff es [haskells]+runSelectI = runSelectExplicit (O.runInferrable def)++{- | Interpret the 'Opaleye' effect using 'Conn.WithConnection' from+[effectful-postgresql](https://hackage.haskell.org/package/effectful-postgresql).++If you don't want to use 'Conn.WithConnection', see 'runOpaleyeConnection'.+-}+runOpaleyeWithConnection ::+ (HasCallStack, Conn.WithConnection :> es, IOE :> es) =>+ Eff (Opaleye : es) a ->+ Eff es a+runOpaleyeWithConnection = interpret $ \env -> \case+ RunSelectExplicit ff sel -> Conn.withConnection $ \conn -> liftIO $ O.runSelectExplicit ff conn sel+ RunSelectFoldExplicit ff sel initial foldFn ->+ Conn.withConnection $ \conn ->+ localSeqUnliftIO env $ \unlift ->+ liftIO $ O.runSelectFoldExplicit ff conn sel initial (\acc new -> unlift $ foldFn acc new)+ RunInsert sel -> Conn.withConnection $ \conn -> liftIO $ O.runInsert conn sel+ RunDelete sel -> Conn.withConnection $ \conn -> liftIO $ O.runDelete conn sel+ RunUpdate sel -> Conn.withConnection $ \conn -> liftIO $ O.runUpdate conn sel++-- | Interpret the 'Opaleye' effect by simply supplying a 'PSQL.Connection'+runOpaleyeConnection ::+ (HasCallStack, IOE :> es) =>+ PSQL.Connection ->+ Eff (Opaleye : es) a ->+ Eff es a+runOpaleyeConnection conn = interpret $ \env -> \case+ RunSelectExplicit ff sel -> liftIO $ O.runSelectExplicit ff conn sel+ RunSelectFoldExplicit ff sel initial foldFn ->+ localSeqUnliftIO env $ \unlift ->+ liftIO $ O.runSelectFoldExplicit ff conn sel initial (\acc new -> unlift $ foldFn acc new)+ RunInsert sel -> liftIO $ O.runInsert conn sel+ RunDelete sel -> liftIO $ O.runDelete conn sel+ RunUpdate sel -> liftIO $ O.runUpdate conn sel