-
Notifications
You must be signed in to change notification settings - Fork 32
/
Indexed.hs
126 lines (109 loc) · 3.24 KB
/
Indexed.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
{-|
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 j m. (i ~ j, Monad m) => Monad (t i j m)
, forall i j. i ~ j => MonadTrans (t i j)
) => 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