packages feed

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 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