{-| Module: Squeal.PostgreSQL.Session.Indexed Description: indexed session monad Copyright: (c) Eitan Chatav, 2019 Maintainer: eitan@morphism.tech Stability: experimental `Squeal.PostgreSQL.Indexed` provides an indexed monad transformer class and a class extending it to run `Definition`s. -} {-# LANGUAGE DataKinds , DefaultSignatures , FlexibleContexts , FlexibleInstances , FunctionalDependencies , PolyKinds , MultiParamTypeClasses , QuantifiedConstraints , RankNTypes , TypeApplications , TypeFamilies , UndecidableInstances #-} module Squeal.PostgreSQL.Session.Indexed ( IndexedMonadTrans (..) , Indexed (..) , IndexedMonadTransPQ (..) , indexedDefine ) where import Control.Category (Category (..)) import Control.Monad import Control.Monad.IO.Class import Control.Monad.Trans import Data.Function ((&)) import Prelude hiding (id, (.)) import Squeal.PostgreSQL.Definition {- | An [Atkey indexed monad] (https://bentnib.org/paramnotions-jfp.pdf) is a `Functor` [enriched category] (https://ncatlab.org/nlab/show/enriched+category). An indexed monad transformer transforms a `Monad` into an indexed monad, and is a monad transformer when its source and target are the same, enabling use of standard @do@ notation for endo-index operations. -} class ( forall i j m. Monad m => Functor (t i j m) , forall i m. Monad m => Monad (t i i m) , forall i. MonadTrans (t i i) ) => IndexedMonadTrans t where {-# MINIMAL pqJoin | pqBind #-} -- | indexed analog of `<*>` pqAp :: Monad m => t i j m (x -> y) -> t j k m x -> t i k m y pqAp tf tx = pqBind (<$> tx) tf -- | indexed analog of `join` pqJoin :: Monad m => t i j m (t j k m y) -> t i k m y pqJoin t = t & pqBind id -- | indexed analog of `=<<` pqBind :: Monad m => (x -> t j k m y) -> t i j m x -> t i k m y pqBind f t = pqJoin (f <$> t) -- | indexed analog of flipped `>>` pqThen :: Monad m => t j k m y -> t i j m x -> t i k m y pqThen pq2 pq1 = pq1 & pqBind (\ _ -> pq2) -- | indexed analog of `<=<` pqAndThen :: Monad m => (y -> t j k m z) -> (x -> t i j m y) -> x -> t i k m z pqAndThen g f x = pqBind g (f x) {- | `Indexed` reshuffles the type parameters of an `IndexedMonadTrans`, exposing its `Category` instance.-} newtype Indexed t m r i j = Indexed {runIndexed :: t i j m r} instance ( IndexedMonadTrans t , Monad m , Monoid r ) => Category (Indexed t m r) where id = Indexed (pure mempty) Indexed g . Indexed f = Indexed $ pqAp (fmap (<>) f) g {- | `IndexedMonadTransPQ` is a class for indexed monad transformers that support running `Definition`s using `define` which acts functorially in effect. * @define id = return ()@ * @define (statement1 >>> statement2) = define statement1 & pqThen (define statement2)@ -} class IndexedMonadTrans pq => IndexedMonadTransPQ pq where define :: MonadIO io => Definition db0 db1 -> pq db0 db1 io () {- | Run a pure SQL `Definition` functorially in effect * @indexedDefine id = id@ * @indexedDefine (def1 >>> def2) = indexedDefine def1 >>> indexedDefine def2@ -} indexedDefine :: (IndexedMonadTransPQ pq, MonadIO io) => Definition db0 db1 -> Indexed pq io () db0 db1 indexedDefine = Indexed . define