content: {-# 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 (Expr a) = Expr a
  
  
  -- | Assume that a nullable column's value is non-null. If the column is
  -- actually @null@, this will lead to runtime errors when you try to decode
  -- the value into Haskell, so you should prefer to use 'Rel8.nullable'
  -- unless you know what you're doing.
  unsafeUnnullify :: Expr (Maybe a) -> Expr a
  unsafeUnnullify (Expr a) = Expr a
  
  
  -- | Like 'maybe', but to eliminate @null@.
  nullableExpr :: Expr b -> (Expr a -> Expr b) -> Expr (Maybe a) -> Expr b
  nullableExpr b f ma = boolExpr (f (unsafeUnnullify ma)) b (isNull ma)
  
  
  nullableOf :: DBType a => Maybe (Expr a) -> Expr (Maybe a)
  nullableOf = maybe null nullify
  
  
  -- | Like 'Data.Maybe.isNothing', but for @null@.
  isNull :: Expr (Maybe a) -> Expr Bool
  isNull = mapPrimExpr (Opaleye.UnExpr Opaleye.OpIsNull)
  
  
  -- | Like 'Data.Maybe.isJust', but for @null@.
  isNonNull :: Expr (Maybe a) -> Expr Bool
  isNonNull = mapPrimExpr (Opaleye.UnExpr 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 f ma = boolExpr (unsafeMapNull f ma) null (isNull 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 f ma mb =
    boolExpr (unsafeLiftOpNull f ma mb) null
      (isNull ma ||. isNull mb)
  {-# INLINABLE liftOpNull #-}
  
  
  snull :: TypeInformation a -> Expr (Maybe a)
  snull info = scastExpr info $ Expr $ Opaleye.ConstExpr Opaleye.NullLit
  
  
  -- | Corresponds to SQL @null@.
  null :: DBType a => Expr (Maybe a)
  null = snull typeInformation
  
  
  unsafeMapNull :: NotNull b
    => (Expr a -> Expr b) -> Expr (Maybe a) -> Expr (Maybe b)
  unsafeMapNull f ma = nullify (f (unsafeUnnullify ma))
  
  
  unsafeLiftOpNull :: NotNull c
    => (Expr a -> Expr b -> Expr c)
    -> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
  unsafeLiftOpNull f ma mb =
    nullify (f (unsafeUnnullify ma) (unsafeUnnullify mb))
  