content: {-# language GADTs #-}
  
  module Rel8.Expr.Bool
    ( false, true
    , (&&.), (||.), not_
    , and_, or_
    , boolExpr
    , caseExpr
    , coalesce
    )
  where
  
  -- base
  import Data.Foldable ( foldl' )
  import Prelude hiding ( null )
  
  -- opaleye
  import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
  
  -- rel8
  import {-# SOURCE #-} Rel8.Expr ( Expr( Expr ) )
  import Rel8.Expr.Opaleye ( mapPrimExpr, toPrimExpr, zipPrimExprsWith )
  import Rel8.Expr.Serialize ( litExpr )
  
  
  -- | The SQL @false@ literal.
  false :: Expr Bool
  false = litExpr False
  
  
  -- | The SQL @true@ literal.
  true :: Expr Bool
  true = litExpr True
  
  
  -- | The SQL @AND@ operator.
  (&&.) :: Expr Bool -> Expr Bool -> Expr Bool
  (&&.) = zipPrimExprsWith (Opaleye.BinExpr Opaleye.OpAnd)
  infixr 3 &&.
  
  
  -- | The SQL @OR@ operator.
  (||.) :: Expr Bool -> Expr Bool -> Expr Bool
  (||.) = zipPrimExprsWith (Opaleye.BinExpr Opaleye.OpOr)
  infixr 2 ||.
  
  
  -- | The SQL @NOT@ operator.
  not_ :: Expr Bool -> Expr Bool
  not_ = mapPrimExpr (Opaleye.UnExpr Opaleye.OpNot)
  
  
  -- | Fold @AND@ over a collection of expressions.
  and_ :: Foldable f => f (Expr Bool) -> Expr Bool
  and_ = foldl' (&&.) true
  
  
  -- | Fold @OR@ over a collection of expressions.
  or_ :: Foldable f => f (Expr Bool) -> Expr Bool
  or_ = foldl' (||.) false
  
  
  -- | Eliminate a boolean-valued expression.
  --
  -- Corresponds to 'Data.Bool.bool'.
  boolExpr :: Expr a -> Expr a -> Expr Bool -> Expr a
  boolExpr ifFalse ifTrue condition = caseExpr [(condition, ifTrue)] ifFalse
  
  
  -- | A multi-way if/then/else statement. The first argument to @caseExpr@ is a
  -- list of alternatives. The first alternative that is of the form @(true, x)@
  -- will be returned. If no such alternative is found, a fallback expression is
  -- returned.
  --
  -- Corresponds to a @CASE@ expression in SQL.
  caseExpr :: [(Expr Bool, Expr a)] -> Expr a -> Expr a
  caseExpr branches (Expr fallback) =
    Expr $ Opaleye.CaseExpr (map go branches) fallback
    where
      go (condition, value) = (toPrimExpr condition, toPrimExpr value)
  
  
  -- | Convert a @Expr (Maybe Bool)@ to a @Expr Bool@ by treating @Nothing@ as
  -- @False@. This can be useful when combined with 'Rel8.where_', which expects
  -- a @Bool@, and produces expressions that optimize better than general case
  -- analysis.
  coalesce :: Expr (Maybe Bool) -> Expr Bool
  coalesce (Expr a) = Expr a &&. Expr (Opaleye.FunExpr "COALESCE" [a, untrue])
    where
      untrue = Opaleye.ConstExpr (Opaleye.BoolLit False)
  