packages feed

rel8-1.6.0.0: src/Rel8/Table/Nullify.hs

{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language LambdaCase #-}
{-# language MultiParamTypeClasses #-}
{-# language NamedFieldPuns #-}
{-# language ScopedTypeVariables #-}
{-# language StandaloneKindSignatures #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}

module Rel8.Table.Nullify
  ( Nullify
  , aggregateNullify
  , guard
  , isNull
  , unsafeUnnullifyTable
  )
where

-- base
import Control.Applicative ( liftA2 )
import Data.Functor.Const ( Const( Const ), getConst )
import Data.Functor.Identity ( runIdentity )
import Data.Kind ( Type )
import Prelude

-- comonad
import Control.Comonad ( Comonad, duplicate, extract, ComonadApply, (<@>) )

-- profunctors
import Data.Profunctor (dimap)

-- rel8
import Rel8.Aggregate (Aggregator')
import Rel8.Expr ( Expr )
import Rel8.Expr.Bool ( (||.), false )
import qualified Rel8.Expr.Null as Expr
import Rel8.Kind.Context ( Reifiable, contextSing )
import Rel8.Schema.Context.Nullify
  ( Nullifiability( NExpr )
  , NonNullifiability
  , Nullifiable, nullifiability
  , nullifiableOrNot, absurd
  , guarder
  , nullifier
  , unnullifier
  )
import Rel8.Schema.Dict ( Dict( Dict ) )
import Rel8.Schema.HTable ( HTable )
import Rel8.Schema.HTable.Nullify
  ( HNullify, hnulls, hnullify, hunnullify
  , hguard
  , hproject
  )
import qualified Rel8.Schema.Kind as K
import Rel8.Schema.Null ( Nullity( NotNull, Null ) )
import qualified Rel8.Schema.Result as R
import Rel8.Table
  ( Table, Columns, Context, toColumns, fromColumns
  , FromExprs, fromResult, toResult
  , Transpose
  )
import Rel8.Schema.Spec ( Spec( Spec, nullity ) )
import Rel8.Table.Eq ( EqTable, eqTable )
import Rel8.Table.Ord ( OrdTable, ordTable )
import Rel8.Table.Projection ( Projectable, apply, project )

-- semigroupoids
import Data.Functor.Apply ( Apply, (<.>), liftF2 )
import Data.Functor.Bind ( Bind, (>>-) )
import Data.Functor.Extend ( Extend, duplicated )


type Nullify :: K.Context -> Type -> Type
data Nullify context a
  = Table (Nullifiability context) a
  | Fields (NonNullifiability context) (HNullify (Columns a) (Context a))


instance Projectable (Nullify context) where
  project f = \case
    Table nullifiable a -> Table nullifiable (fromColumns (apply f (toColumns a)))
    Fields nonNullifiable a -> Fields nonNullifiable (hproject (apply f) a)


instance Nullifiable context => Functor (Nullify context) where
  fmap f = \case
    Table nullifiable a -> Table nullifiable (f a)
    Fields notNullifiable _ -> absurd nullifiability notNullifiable


instance Nullifiable context => Foldable (Nullify context) where
  foldMap f = \case
    Table _ a -> f a
    Fields notNullifiable _ -> absurd nullifiability notNullifiable


instance Nullifiable context => Traversable (Nullify context) where
  traverse f = \case
    Table nullifiable a -> Table nullifiable <$> f a
    Fields notNullifiable _ -> absurd nullifiability notNullifiable


instance Nullifiable context => Apply (Nullify context) where
  liftF2 f = \case
    Table nullifiable a -> \case
      Table _ b -> Table nullifiable (f a b)
      Fields notNullifiable _ -> absurd nullifiable notNullifiable
    Fields notNullifiable _ -> absurd nullifiability notNullifiable


instance Nullifiable context => Applicative (Nullify context) where
  pure = Table nullifiability
  liftA2 = liftF2


instance Nullifiable context => Bind (Nullify context) where
  Table _ a >>- f = f a
  Fields notNullifiable _ >>- _ = absurd nullifiability notNullifiable


instance Nullifiable context => Monad (Nullify context) where
  (>>=) = (>>-)


instance Nullifiable context => Extend (Nullify context) where
  duplicated = \case
    Table nullifiable a -> Table nullifiable (Table nullifiable a)
    Fields notNullifiable _ -> absurd nullifiability notNullifiable


instance Nullifiable context => Comonad (Nullify context) where
  extract = \case
    Table _ a -> a
    Fields notNullifiable _ -> absurd nullifiability notNullifiable
  duplicate = duplicated


instance Nullifiable context => ComonadApply (Nullify context) where
  (<@>) = (<.>)


instance (Table context a, Reifiable context, context ~ context') =>
  Table context' (Nullify context a)
 where
  type Columns (Nullify context a) = HNullify (Columns a)
  type Context (Nullify context a) = Context a
  type FromExprs (Nullify context a) = Maybe (FromExprs a)
  type Transpose to (Nullify context a) = Nullify to (Transpose to a)

  fromColumns = case nullifiableOrNot contextSing of
    Left notNullifiable -> Fields notNullifiable
    Right nullifiable ->
      Table nullifiable .
      fromColumns .
      runIdentity .
      hunnullify (\spec -> pure . unnullifier nullifiable spec)

  toColumns = \case
    Table nullifiable a -> hnullify (nullifier nullifiable) (toColumns a)
    Fields _ a -> a

  fromResult = fmap (fromResult @_ @a) . hunnullify R.unnullifier

  toResult =
    maybe (hnulls (const R.null)) (hnullify R.nullifier) .
    fmap (toResult @_ @a)


instance (EqTable a, context ~ Expr) => EqTable (Nullify context a) where
  eqTable = hnullify (\_ Dict -> Dict) (eqTable @a)


instance (OrdTable a, context ~ Expr) => OrdTable (Nullify context a) where
  ordTable = hnullify (\_ Dict -> Dict) (ordTable @a)


aggregateNullify :: ()
  => Aggregator' fold i a
  -> Aggregator' fold (Nullify Expr i) (Nullify Expr a)
aggregateNullify = dimap from to
  where
    from = unsafeUnnullifyTable
    to = Table NExpr


guard :: (Reifiable context, HTable t)
  => context tag
  -> (tag -> Bool)
  -> (Expr tag -> Expr Bool)
  -> HNullify t context
  -> HNullify t context
guard tag isNonNull isNonNullExpr =
  hguard (guarder contextSing tag isNonNull isNonNullExpr)


isNull :: forall a. Table Expr a => Nullify Expr a -> Expr Bool
isNull =
  maybe false getAny .
  getConst .
  hunnullify (\Spec {nullity} a -> Const $ case nullity of
    NotNull -> Just $ Any $ Expr.isNull a
    Null -> Nothing) .
  toColumns


unsafeUnnullifyTable :: Nullify Expr a -> a
unsafeUnnullifyTable = \case
  Table _ a -> a
  Fields notNullifiable _ -> absurd NExpr notNullifiable


newtype Any = Any
  { getAny :: Expr Bool
  }


instance Semigroup Any where
  Any a <> Any b = Any (a ||. b)