opaleye-0.7.0.0: src/Opaleye/Internal/QueryArr.hs
{-# LANGUAGE Arrows #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Opaleye.Internal.QueryArr where
import Prelude hiding (id)
import qualified Opaleye.Internal.Unpackspec as U
import qualified Opaleye.Internal.Tag as Tag
import Opaleye.Internal.Tag (Tag)
import qualified Opaleye.Internal.PrimQuery as PQ
import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ
import qualified Control.Arrow as Arr
import Control.Arrow ((&&&), (***), arr, returnA)
import qualified Control.Category as C
import Control.Category ((<<<), id)
import Control.Applicative (Applicative, pure, (<*>))
import Data.List.NonEmpty ( NonEmpty((:|)) )
import qualified Data.Profunctor as P
import qualified Data.Profunctor.Product as PP
-- Ideally this should be wrapped in a monad which automatically
-- increments the Tag, but I couldn't be bothered to do that.
-- | A parametrised 'Select'. A @SelectArr a b@ accepts an argument
-- of type @a@.
--
-- @SelectArr a b@ is analogous to a Haskell function @a -> [b]@.
newtype SelectArr a b = QueryArr ((a, PQ.PrimQuery, Tag) -> (b, PQ.PrimQuery, Tag))
type QueryArr = SelectArr
type Query = SelectArr ()
productQueryArr :: ((a, Tag) -> (b, PQ.PrimQuery, Tag)) -> QueryArr a b
productQueryArr f = QueryArr g
where g (a0, primQuery, t0) = (a1, PQ.times primQuery primQuery', t1)
where (a1, primQuery', t1) = f (a0, t0)
{-# DEPRECATED simpleQueryArr "Use 'productQueryArr' instead. Its name indicates better what it actually does" #-}
simpleQueryArr :: ((a, Tag) -> (b, PQ.PrimQuery, Tag)) -> QueryArr a b
simpleQueryArr = productQueryArr
runQueryArr :: QueryArr a b -> (a, PQ.PrimQuery, Tag) -> (b, PQ.PrimQuery, Tag)
runQueryArr (QueryArr f) = f
runSimpleQueryArr :: QueryArr a b -> (a, Tag) -> (b, PQ.PrimQuery, Tag)
runSimpleQueryArr f (a, t) = runQueryArr f (a, PQ.Unit, t)
runSimpleQueryArrStart :: QueryArr a b -> a -> (b, PQ.PrimQuery, Tag)
runSimpleQueryArrStart q a = runSimpleQueryArr q (a, Tag.start)
runQueryArrUnpack :: U.Unpackspec a b
-> Query a -> ([HPQ.PrimExpr], PQ.PrimQuery, Tag)
runQueryArrUnpack unpackspec q = (primExprs, primQ, endTag)
where (columns, primQ, endTag) = runSimpleQueryArrStart q ()
primExprs = U.collectPEs unpackspec columns
first3 :: (a1 -> b) -> (a1, a2, a3) -> (b, a2, a3)
first3 f (a1, a2, a3) = (f a1, a2, a3)
-- | A @SELECT@, i.e. an SQL query which produces a collection of
-- rows.
--
-- @Select a@ is analogous to a Haskell value @[a]@.
type Select = SelectArr ()
-- | Implements @LATERAL@ subqueries.
--
-- You might find it easier to use 'Opaleye.Lateral.laterally' (if you
-- want to apply 'Opaleye.Aggregate.aggregate',
-- 'Opaleye.Order.orderBy' or 'Opaleye.Order.limit' to a 'SelectArr')
-- or 'Opaleye.Lateral.bilaterally' (if you want to apply
-- 'Opaleye.Binary.union', 'Opaleye.Binary.intersect' and
-- 'Opaleye.Binary.except' to two 'SelectArr's).
lateral :: (i -> Select a) -> SelectArr i a
lateral f = QueryArr qa
where
qa (i, primQueryL, tag) = (a, primQueryJoin, tag')
where
(a, primQueryR, tag') = runSimpleQueryArr (f i) ((), tag)
primQueryJoin = PQ.Product ((PQ.NonLateral, primQueryL)
:| [(PQ.Lateral, primQueryR)])
[]
-- | Convert an arrow argument into a function argument so that it can
-- be applied inside @do@-notation rather than arrow notation.
viaLateral :: SelectArr i a -> i -> Select a
viaLateral s i = s <<< pure i
bind :: SelectArr i a -> (a -> SelectArr i b) -> SelectArr i b
bind s f = proc i -> do
a <- s -< i
b <- lateral (\(a, i) -> viaLateral (f a) i) -< (a, i)
returnA -< b
arrowApply :: SelectArr (SelectArr i a, i) a
arrowApply = lateral (\(f, i) -> viaLateral f i)
instance C.Category QueryArr where
id = QueryArr id
QueryArr f . QueryArr g = QueryArr (f . g)
instance Arr.Arrow QueryArr where
arr f = QueryArr (first3 f)
first f = QueryArr g
where g ((b, d), primQ, t0) = ((c, d), primQ', t1)
where (c, primQ', t1) = runQueryArr f (b, primQ, t0)
instance Arr.ArrowChoice QueryArr where
left f = QueryArr g
where g (e, primQ, t0) = case e of
Left a -> first3 Left (runQueryArr f (a, primQ, t0))
Right b -> (Right b, primQ, t0)
instance Arr.ArrowApply QueryArr where
app = arrowApply
instance Functor (QueryArr a) where
fmap f = (arr f <<<)
instance Applicative (QueryArr a) where
pure = arr . const
f <*> g = arr (uncurry ($)) <<< (f &&& g)
instance Monad (QueryArr a) where
return = pure
(>>=) = bind
instance P.Profunctor QueryArr where
dimap f g a = arr g <<< a <<< arr f
instance PP.ProductProfunctor QueryArr where
empty = id
(***!) = (***)