packages feed

rel8-1.0.0.0: src/Rel8/Expr/Bool.hs

{-# 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)