dataframe-core-1.0.2.0: src/DataFrame/Operators.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module DataFrame.Operators where
import Data.Function ((&))
import qualified Data.Text as T
import DataFrame.Internal.Column (Columnable)
import DataFrame.Internal.Expression (
BinUDF (MkBinaryOp),
BinaryOp (
binaryCommutative,
binaryFn,
binaryName,
binaryPrecedence,
binarySymbol
),
Expr (Binary, Col, If, Lit, Unary),
NamedExpr,
UExpr (UExpr),
UnUDF (MkUnaryOp),
)
import DataFrame.Internal.Nullable (
BaseType,
DivWidenOp,
NullCmpResult,
NullLift2Op (applyNull2),
NullableCmpOp (nullCmpOp),
NumericWidenOp,
WidenResult,
WidenResultDiv,
divArithOp,
widenArithOp,
widenCmpOp,
)
import DataFrame.Internal.Types (Promote, PromoteDiv)
infixr 8 .^^, .^^., .^, .^.
infixl 7 .*, ./, .*., ./.
infixl 6 .+, .-, .+., .-.
infix 4 .==, .==., .<, .<., .<=, .<=., .>=, .>=., .>, .>., ./=, ./=.
infixr 3 .&&, .&&.
infixr 2 .||, .||.
infixr 0 .=
(|>) :: a -> (a -> b) -> b
(|>) = (&)
as :: (Columnable a) => Expr a -> T.Text -> NamedExpr
as expr colName = (colName, UExpr expr)
name :: (Show a) => Expr a -> T.Text
name (Col n) = n
name other =
error $
"You must call `name` on a column reference. Not the expression: " ++ show other
col :: (Columnable a) => T.Text -> Expr a
col = Col
ifThenElse :: (Columnable a) => Expr Bool -> Expr a -> Expr a -> Expr a
ifThenElse = If
lit :: (Columnable a) => a -> Expr a
lit = Lit
(.=) :: (Columnable a) => T.Text -> Expr a -> NamedExpr
(.=) = flip as
liftDecorated ::
(Columnable a, Columnable b) =>
(a -> b) -> T.Text -> Maybe T.Text -> Expr a -> Expr b
liftDecorated f opName rep = Unary (MkUnaryOp f opName rep)
lift2Decorated ::
(Columnable c, Columnable b, Columnable a) =>
(c -> b -> a) ->
T.Text ->
Maybe T.Text ->
Bool ->
Int ->
Expr c ->
Expr b ->
Expr a
lift2Decorated f opName rep comm prec =
Binary (MkBinaryOp f opName rep comm prec)
data NullEq a b c where
NullEq ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Eq (Promote (BaseType a) (BaseType b))
) =>
NullEq a b (NullCmpResult a b)
data NullNeq a b c where
NullNeq ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Eq (Promote (BaseType a) (BaseType b))
) =>
NullNeq a b (NullCmpResult a b)
data NullLt a b c where
NullLt ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
NullLt a b (NullCmpResult a b)
data NullGt a b c where
NullGt ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
NullGt a b (NullCmpResult a b)
data NullLeq a b c where
NullLeq ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
NullLeq a b (NullCmpResult a b)
data NullGeq a b c where
NullGeq ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
NullGeq a b (NullCmpResult a b)
data NullAnd a b c where
NullAnd ::
(NullableCmpOp a b (NullCmpResult a b), BaseType a ~ Bool) =>
NullAnd a b (NullCmpResult a b)
data NullOr a b c where
NullOr ::
(NullableCmpOp a b (NullCmpResult a b), BaseType a ~ Bool) =>
NullOr a b (NullCmpResult a b)
instance BinaryOp NullEq where
binaryFn NullEq = applyNull2 (widenCmpOp (==))
binaryName NullEq = "eq"
binarySymbol NullEq = Just ".=="
binaryCommutative NullEq = True
binaryPrecedence NullEq = 4
instance BinaryOp NullNeq where
binaryFn NullNeq = applyNull2 (widenCmpOp (/=))
binaryName NullNeq = "neq"
binarySymbol NullNeq = Just "./="
binaryCommutative NullNeq = True
binaryPrecedence NullNeq = 4
instance BinaryOp NullLt where
binaryFn NullLt = applyNull2 (widenCmpOp (<))
binaryName NullLt = "lt"
binarySymbol NullLt = Just ".<"
binaryPrecedence NullLt = 4
instance BinaryOp NullGt where
binaryFn NullGt = applyNull2 (widenCmpOp (>))
binaryName NullGt = "gt"
binarySymbol NullGt = Just ".>"
binaryPrecedence NullGt = 4
instance BinaryOp NullLeq where
binaryFn NullLeq = applyNull2 (widenCmpOp (<=))
binaryName NullLeq = "leq"
binarySymbol NullLeq = Just ".<="
binaryPrecedence NullLeq = 4
instance BinaryOp NullGeq where
binaryFn NullGeq = applyNull2 (widenCmpOp (>=))
binaryName NullGeq = "geq"
binarySymbol NullGeq = Just ".>="
binaryPrecedence NullGeq = 4
instance BinaryOp NullAnd where
binaryFn NullAnd = nullCmpOp (&&)
binaryName NullAnd = "nulland"
binarySymbol NullAnd = Just ".&&"
binaryCommutative NullAnd = True
binaryPrecedence NullAnd = 3
instance BinaryOp NullOr where
binaryFn NullOr = nullCmpOp (||)
binaryName NullOr = "nullor"
binarySymbol NullOr = Just ".||"
binaryCommutative NullOr = True
binaryPrecedence NullOr = 2
(.==.) ::
(Columnable a, Eq a) =>
Expr a ->
Expr a ->
Expr Bool
(.==.) = lift2Decorated (==) "eq" (Just ".==.") True 4
(./=.) ::
(Columnable a, Eq a) =>
Expr a ->
Expr a ->
Expr Bool
(./=.) = lift2Decorated (/=) "neq" (Just "./=.") True 4
(.<.) ::
(Columnable a, Ord a) =>
Expr a ->
Expr a ->
Expr Bool
(.<.) = lift2Decorated (<) "lt" (Just ".<.") False 4
(.>.) ::
(Columnable a, Ord a) =>
Expr a ->
Expr a ->
Expr Bool
(.>.) = lift2Decorated (>) "gt" (Just ".>.") False 4
(.<=.) ::
(Columnable a, Ord a) =>
Expr a ->
Expr a ->
Expr Bool
(.<=.) = lift2Decorated (<=) "leq" (Just ".<=.") False 4
(.>=.) ::
(Columnable a, Ord a) =>
Expr a ->
Expr a ->
Expr Bool
(.>=.) = lift2Decorated (>=) "geq" (Just ".>=.") False 4
(.+.) :: (Columnable a, Num a) => Expr a -> Expr a -> Expr a
(.+.) = (+)
(.-.) :: (Columnable a, Num a) => Expr a -> Expr a -> Expr a
(.-.) = (-)
(.*.) :: (Columnable a, Num a) => Expr a -> Expr a -> Expr a
(.*.) = (*)
(./.) :: (Columnable a, Fractional a) => Expr a -> Expr a -> Expr a
(./.) = (/)
-- Nullable-aware arithmetic operators
{- | Nullable-aware addition. Works for all combinations of nullable\/non-nullable operands.
@col \@Int "x" .+ col \@(Maybe Int) "y" -- :: Expr (Maybe Int)@
-}
(.+) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b (Promote (BaseType a) (BaseType b)) (WidenResult a b)
, Num (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (WidenResult a b)
(.+) = lift2Decorated (applyNull2 (widenArithOp (+))) "nulladd" (Just ".+") True 6
-- | Nullable-aware subtraction.
(.-) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b (Promote (BaseType a) (BaseType b)) (WidenResult a b)
, Num (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (WidenResult a b)
(.-) = lift2Decorated (applyNull2 (widenArithOp (-))) "nullsub" (Just ".-") False 6
-- | Nullable-aware multiplication.
(.*) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b (Promote (BaseType a) (BaseType b)) (WidenResult a b)
, Num (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (WidenResult a b)
(.*) = lift2Decorated (applyNull2 (widenArithOp (*))) "nullmul" (Just ".*") True 7
-- | Nullable-aware division. Integral operands are promoted to Double.
(./) ::
( DivWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b (PromoteDiv (BaseType a) (BaseType b)) (WidenResultDiv a b)
, Fractional (PromoteDiv (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (WidenResultDiv a b)
(./) = lift2Decorated (applyNull2 (divArithOp (/))) "nulldiv" (Just "./") False 7
-- Nullable-aware comparison operators (three-valued logic: Nothing if either operand is Nothing)
{- | Nullable-aware equality. Widens numeric operands to their common type,
so @Expr Double .== Expr Int@ typechecks. Returns @Maybe Bool@ when either
operand is nullable.
-}
(.==) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Eq (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(.==) = Binary NullEq
-- | Nullable-aware inequality. Widens numeric operands to their common type.
(./=) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Eq (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(./=) = Binary NullNeq
-- | Nullable-aware less-than. Widens numeric operands to their common type.
(.<) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(.<) = Binary NullLt
-- | Nullable-aware greater-than. Widens numeric operands to their common type.
(.>) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(.>) = Binary NullGt
{- | Nullable-aware less-than-or-equal. Widens numeric operands to their
common type, so @Expr Double .<= Expr Int@ typechecks.
-}
(.<=) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(.<=) = Binary NullLeq
-- | Nullable-aware greater-than-or-equal. Widens numeric operands to their common type.
(.>=) ::
( NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b Bool (NullCmpResult a b)
, Ord (Promote (BaseType a) (BaseType b))
) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(.>=) = Binary NullGeq
(.&&.) :: Expr Bool -> Expr Bool -> Expr Bool
(.&&.) = lift2Decorated (&&) "and" (Just ".&&.") True 3
(.||.) :: Expr Bool -> Expr Bool -> Expr Bool
(.||.) = lift2Decorated (||) "or" (Just ".||.") True 2
-- | Nullable-aware logical AND. Returns @Maybe Bool@ when either operand is nullable.
(.&&) ::
(NullableCmpOp a b (NullCmpResult a b), BaseType a ~ Bool) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(.&&) = Binary NullAnd
-- | Nullable-aware logical OR. Returns @Maybe Bool@ when either operand is nullable.
(.||) ::
(NullableCmpOp a b (NullCmpResult a b), BaseType a ~ Bool) =>
Expr a ->
Expr b ->
Expr (NullCmpResult a b)
(.||) = Binary NullOr
(.^^) ::
( Columnable (BaseType a)
, Columnable (BaseType b)
, Fractional (BaseType a)
, Integral (BaseType b)
, NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b (BaseType a) a
, Num (Promote (BaseType a) (BaseType b))
) =>
Expr a -> Expr b -> Expr a
(.^^) = lift2Decorated (applyNull2 (^^)) "pow" (Just ".^^") False 8
(.^) ::
( Columnable (BaseType a)
, Columnable (BaseType b)
, Num (BaseType a)
, Integral (BaseType b)
, NumericWidenOp (BaseType a) (BaseType b)
, NullLift2Op a b (BaseType a) a
, Num (Promote (BaseType a) (BaseType b))
) =>
Expr a -> Expr b -> Expr a
(.^) = lift2Decorated (applyNull2 (^)) "pow" (Just ".^") False 8
-- Same-type (non-nullable) exponentiation operators
(.^^.) ::
(Columnable a, Columnable b, Fractional a, Integral b) =>
Expr a -> Expr b -> Expr a
(.^^.) = lift2Decorated (^^) "pow" (Just ".^^.") False 8
(.^.) ::
(Columnable a, Columnable b, Num a, Integral b) =>
Expr a -> Expr b -> Expr a
(.^.) = lift2Decorated (^) "pow" (Just ".^.") False 8