{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language TypeFamilies #-}

{-# options -fno-warn-redundant-constraints #-}

module Rel8.Expr.Null
  ( null, snull, nullableExpr, nullableOf
  , isNull, isNonNull
  , nullify, unsafeUnnullify
  , mapNull, liftOpNull
  , unsafeMapNull, unsafeLiftOpNull
  )
where

-- base
import Prelude hiding ( null )

-- opaleye
import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye

-- rel8
import {-# SOURCE #-} Rel8.Expr ( Expr( Expr ) )
import Rel8.Expr.Bool ( (||.), boolExpr )
import Rel8.Expr.Opaleye ( scastExpr, mapPrimExpr )
import Rel8.Schema.Null ( NotNull )
import Rel8.Type ( DBType, typeInformation )
import Rel8.Type.Information ( TypeInformation )


-- | Lift an expression that can't be @null@ to a type that might be @null@.
-- This is an identity operation in terms of any generated query, and just
-- modifies the query's type.
nullify :: NotNull a => Expr a -> Expr (Maybe a)
nullify :: forall a. NotNull a => Expr a -> Expr (Maybe a)
nullify (Expr PrimExpr
a) = forall a. PrimExpr -> Expr a
Expr PrimExpr
a


unsafeUnnullify :: Expr (Maybe a) -> Expr a
unsafeUnnullify :: forall a. Expr (Maybe a) -> Expr a
unsafeUnnullify (Expr PrimExpr
a) = forall a. PrimExpr -> Expr a
Expr PrimExpr
a


-- | Like 'maybe', but to eliminate @null@.
nullableExpr :: Expr b -> (Expr a -> Expr b) -> Expr (Maybe a) -> Expr b
nullableExpr :: forall b a.
Expr b -> (Expr a -> Expr b) -> Expr (Maybe a) -> Expr b
nullableExpr Expr b
b Expr a -> Expr b
f Expr (Maybe a)
ma = forall a. Expr a -> Expr a -> Expr Bool -> Expr a
boolExpr (Expr a -> Expr b
f (forall a. Expr (Maybe a) -> Expr a
unsafeUnnullify Expr (Maybe a)
ma)) Expr b
b (forall a. Expr (Maybe a) -> Expr Bool
isNull Expr (Maybe a)
ma)


nullableOf :: DBType a => Maybe (Expr a) -> Expr (Maybe a)
nullableOf :: forall a. DBType a => Maybe (Expr a) -> Expr (Maybe a)
nullableOf = forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. DBType a => Expr (Maybe a)
null forall a. NotNull a => Expr a -> Expr (Maybe a)
nullify


-- | Like 'Data.Maybe.isNothing', but for @null@.
isNull :: Expr (Maybe a) -> Expr Bool
isNull :: forall a. Expr (Maybe a) -> Expr Bool
isNull = forall a b. (PrimExpr -> PrimExpr) -> Expr a -> Expr b
mapPrimExpr (UnOp -> PrimExpr -> PrimExpr
Opaleye.UnExpr UnOp
Opaleye.OpIsNull)


-- | Like 'Data.Maybe.isJust', but for @null@.
isNonNull :: Expr (Maybe a) -> Expr Bool
isNonNull :: forall a. Expr (Maybe a) -> Expr Bool
isNonNull = forall a b. (PrimExpr -> PrimExpr) -> Expr a -> Expr b
mapPrimExpr (UnOp -> PrimExpr -> PrimExpr
Opaleye.UnExpr UnOp
Opaleye.OpIsNotNull)


-- | Lift an operation on non-@null@ values to an operation on possibly @null@
-- values. When given @null@, @mapNull f@ returns @null@.
-- 
-- This is like 'fmap' for 'Maybe'.
mapNull :: DBType b
  => (Expr a -> Expr b) -> Expr (Maybe a) -> Expr (Maybe b)
mapNull :: forall b a.
DBType b =>
(Expr a -> Expr b) -> Expr (Maybe a) -> Expr (Maybe b)
mapNull Expr a -> Expr b
f Expr (Maybe a)
ma = forall a. Expr a -> Expr a -> Expr Bool -> Expr a
boolExpr (forall b a.
NotNull b =>
(Expr a -> Expr b) -> Expr (Maybe a) -> Expr (Maybe b)
unsafeMapNull Expr a -> Expr b
f Expr (Maybe a)
ma) forall a. DBType a => Expr (Maybe a)
null (forall a. Expr (Maybe a) -> Expr Bool
isNull Expr (Maybe a)
ma)


-- | Lift a binary operation on non-@null@ expressions to an equivalent binary
-- operator on possibly @null@ expressions. If either of the final arguments
-- are @null@, @liftOpNull@ returns @null@.
--
-- This is like 'liftA2' for 'Maybe'.
liftOpNull :: DBType c
  => (Expr a -> Expr b -> Expr c)
  -> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
liftOpNull :: forall c a b.
DBType c =>
(Expr a -> Expr b -> Expr c)
-> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
liftOpNull Expr a -> Expr b -> Expr c
f Expr (Maybe a)
ma Expr (Maybe b)
mb =
  forall a. Expr a -> Expr a -> Expr Bool -> Expr a
boolExpr (forall c a b.
NotNull c =>
(Expr a -> Expr b -> Expr c)
-> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
unsafeLiftOpNull Expr a -> Expr b -> Expr c
f Expr (Maybe a)
ma Expr (Maybe b)
mb) forall a. DBType a => Expr (Maybe a)
null
    (forall a. Expr (Maybe a) -> Expr Bool
isNull Expr (Maybe a)
ma Expr Bool -> Expr Bool -> Expr Bool
||. forall a. Expr (Maybe a) -> Expr Bool
isNull Expr (Maybe b)
mb)
{-# INLINABLE liftOpNull #-}


snull :: TypeInformation a -> Expr (Maybe a)
snull :: forall a. TypeInformation a -> Expr (Maybe a)
snull TypeInformation a
info = forall a. TypeInformation (Unnullify a) -> Expr a -> Expr a
scastExpr TypeInformation a
info forall a b. (a -> b) -> a -> b
$ forall a. PrimExpr -> Expr a
Expr forall a b. (a -> b) -> a -> b
$ Literal -> PrimExpr
Opaleye.ConstExpr Literal
Opaleye.NullLit


-- | Corresponds to SQL @null@.
null :: DBType a => Expr (Maybe a)
null :: forall a. DBType a => Expr (Maybe a)
null = forall a. TypeInformation a -> Expr (Maybe a)
snull forall a. DBType a => TypeInformation a
typeInformation


unsafeMapNull :: NotNull b
  => (Expr a -> Expr b) -> Expr (Maybe a) -> Expr (Maybe b)
unsafeMapNull :: forall b a.
NotNull b =>
(Expr a -> Expr b) -> Expr (Maybe a) -> Expr (Maybe b)
unsafeMapNull Expr a -> Expr b
f Expr (Maybe a)
ma = forall a. NotNull a => Expr a -> Expr (Maybe a)
nullify (Expr a -> Expr b
f (forall a. Expr (Maybe a) -> Expr a
unsafeUnnullify Expr (Maybe a)
ma))


unsafeLiftOpNull :: NotNull c
  => (Expr a -> Expr b -> Expr c)
  -> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
unsafeLiftOpNull :: forall c a b.
NotNull c =>
(Expr a -> Expr b -> Expr c)
-> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
unsafeLiftOpNull Expr a -> Expr b -> Expr c
f Expr (Maybe a)
ma Expr (Maybe b)
mb =
  forall a. NotNull a => Expr a -> Expr (Maybe a)
nullify (Expr a -> Expr b -> Expr c
f (forall a. Expr (Maybe a) -> Expr a
unsafeUnnullify Expr (Maybe a)
ma) (forall a. Expr (Maybe a) -> Expr a
unsafeUnnullify Expr (Maybe b)
mb))