{-# OPTIONS_HADDOCK not-home #-}

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Opaleye.Internal.Distinct where

import qualified Opaleye.Internal.MaybeFields as M
import           Opaleye.Select (Select)
import           Opaleye.Field (Field_)
import           Opaleye.Aggregate (Aggregator, groupBy, aggregate)

import           Control.Applicative (Applicative, pure, (<*>))

import qualified Data.Profunctor as P
import qualified Data.Profunctor.Product as PP
import           Data.Profunctor.Product.Default (Default, def)

-- We implement distinct simply by grouping by all columns.  We could
-- instead implement it as SQL's DISTINCT but implementing it in terms
-- of something else that we already have is easier at this point.

distinctExplicit :: Distinctspec fields fields'
                 -> Select fields -> Select fields'
distinctExplicit :: forall fields fields'.
Distinctspec fields fields' -> Select fields -> Select fields'
distinctExplicit (Distinctspec Aggregator fields fields'
agg) = forall a b. Aggregator a b -> Select a -> Select b
aggregate Aggregator fields fields'
agg

newtype Distinctspec a b = Distinctspec (Aggregator a b)

instance Default Distinctspec (Field_ n a) (Field_ n a) where
  def :: Distinctspec (Field_ n a) (Field_ n a)
def = forall a b. Aggregator a b -> Distinctspec a b
Distinctspec forall (n :: Nullability) a. Aggregator (Field_ n a) (Field_ n a)
groupBy

distinctspecField :: Distinctspec (Field_ n a) (Field_ n a)
distinctspecField :: forall (n :: Nullability) a. Distinctspec (Field_ n a) (Field_ n a)
distinctspecField = forall (p :: * -> * -> *) a b. Default p a b => p a b
def

distinctspecMaybeFields :: M.WithNulls Distinctspec a b
                        -> Distinctspec (M.MaybeFields a) (M.MaybeFields b)
distinctspecMaybeFields :: forall a b.
WithNulls Distinctspec a b
-> Distinctspec (MaybeFields a) (MaybeFields b)
distinctspecMaybeFields = forall (p :: * -> * -> *) a b.
ProductProfunctor p =>
p (Field SqlBool) (Field SqlBool)
-> WithNulls p a b -> p (MaybeFields a) (MaybeFields b)
M.unWithNulls forall (p :: * -> * -> *) a b. Default p a b => p a b
def

instance Default (M.WithNulls Distinctspec) a b
  => Default Distinctspec (M.MaybeFields a) (M.MaybeFields b) where
  def :: Distinctspec (MaybeFields a) (MaybeFields b)
def = forall a b.
WithNulls Distinctspec a b
-> Distinctspec (MaybeFields a) (MaybeFields b)
distinctspecMaybeFields forall (p :: * -> * -> *) a b. Default p a b => p a b
def

-- { Boilerplate instances

instance Functor (Distinctspec a) where
  fmap :: forall a b. (a -> b) -> Distinctspec a a -> Distinctspec a b
fmap a -> b
f (Distinctspec Aggregator a a
g) = forall a b. Aggregator a b -> Distinctspec a b
Distinctspec (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f Aggregator a a
g)

instance Applicative (Distinctspec a) where
  pure :: forall a. a -> Distinctspec a a
pure = forall a b. Aggregator a b -> Distinctspec a b
Distinctspec forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Applicative f => a -> f a
pure
  Distinctspec Aggregator a (a -> b)
f <*> :: forall a b.
Distinctspec a (a -> b) -> Distinctspec a a -> Distinctspec a b
<*> Distinctspec Aggregator a a
x = forall a b. Aggregator a b -> Distinctspec a b
Distinctspec (Aggregator a (a -> b)
f forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Aggregator a a
x)

instance P.Profunctor Distinctspec where
  dimap :: forall a b c d.
(a -> b) -> (c -> d) -> Distinctspec b c -> Distinctspec a d
dimap a -> b
f c -> d
g (Distinctspec Aggregator b c
q) = forall a b. Aggregator a b -> Distinctspec a b
Distinctspec (forall (p :: * -> * -> *) a b c d.
Profunctor p =>
(a -> b) -> (c -> d) -> p b c -> p a d
P.dimap a -> b
f c -> d
g Aggregator b c
q)

instance PP.ProductProfunctor Distinctspec where
  purePP :: forall b a. b -> Distinctspec a b
purePP = forall (f :: * -> *) a. Applicative f => a -> f a
pure
  **** :: forall a a b.
Distinctspec a (a -> b) -> Distinctspec a a -> Distinctspec a b
(****) = forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
(<*>)

instance PP.SumProfunctor Distinctspec where
  Distinctspec Aggregator a b
x1 +++! :: forall a b a' b'.
Distinctspec a b
-> Distinctspec a' b' -> Distinctspec (Either a a') (Either b b')
+++! Distinctspec Aggregator a' b'
x2 = forall a b. Aggregator a b -> Distinctspec a b
Distinctspec (Aggregator a b
x1 forall (p :: * -> * -> *) a b a' b'.
SumProfunctor p =>
p a b -> p a' b' -> p (Either a a') (Either b b')
PP.+++! Aggregator a' b'
x2)

-- }