rel8-1.0.0.0: src/Rel8/Table/Tag.hs
{-# language DataKinds #-}
{-# language FlexibleInstances #-}
{-# language GADTs #-}
{-# language InstanceSigs #-}
{-# language NamedFieldPuns #-}
{-# language ScopedTypeVariables #-}
{-# language StandaloneKindSignatures #-}
{-# language TypeApplications #-}
{-# language UndecidableInstances #-}
module Rel8.Table.Tag
( Tag(..), Taggable
, fromAggregate
, fromExpr
, fromName
)
where
-- base
import Control.Applicative ( (<|>), empty )
import Data.Kind ( Constraint, Type )
import Data.Foldable ( fold )
import Data.Monoid ( getFirst )
import Data.Proxy ( Proxy( Proxy ) )
import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )
import Prelude
-- rel8
import Rel8.Aggregate ( Aggregate, Aggregator, foldInputs )
import Rel8.Expr ( Expr )
import Rel8.Expr.Opaleye ( fromPrimExpr )
import Rel8.Schema.Name ( Name( Name ) )
import Rel8.Schema.Null ( Sql )
import Rel8.Type.Monoid ( DBMonoid )
type Tag :: Symbol -> Type -> Type
data Tag label a = Tag
{ expr :: Expr a
, aggregator :: Maybe Aggregator
, name :: Name a
}
type Taggable :: Type -> Constraint
class Taggable a where
tappend :: KnownSymbol label => Tag label a -> Tag label a -> Tag label a
tempty :: KnownSymbol label => Tag label a
instance Sql DBMonoid a => Taggable a where
tappend :: forall label. KnownSymbol label
=> Tag label a -> Tag label a -> Tag label a
tappend a b = Tag
{ expr = expr a <> expr b
, aggregator = aggregator a <|> aggregator b
, name = case (name a, symbolVal (Proxy @label)) of
(Name x, y)
| x == y -> name b
| otherwise -> name a
}
{-# INLINABLE tappend #-}
tempty :: forall label. KnownSymbol label => Tag label a
tempty = Tag
{ expr = mempty
, aggregator = empty
, name = Name (symbolVal (Proxy @label))
}
{-# INLINABLE tempty #-}
instance (KnownSymbol label, Taggable a) => Semigroup (Tag label a) where
(<>) = tappend
instance (KnownSymbol label, Taggable a) => Monoid (Tag label a) where
mempty = tempty
fromAggregate :: forall a label. (KnownSymbol label, Taggable a)
=> Aggregate a -> Tag label a
fromAggregate = fold . getFirst . foldInputs go
where
go aggregator primExpr = pure $ (tempty @a @label)
{ expr = fromPrimExpr primExpr
, aggregator
}
fromExpr :: forall label a. (KnownSymbol label, Taggable a)
=> Expr a -> Tag label a
fromExpr expr = (tempty @a @label) {expr}
fromName :: forall a label. Taggable a => Name a -> Tag label a
fromName name = (tempty @a @"") {name}