packages feed

dataframe-expr-serializer 1.2.1.0 → 1.3.0.0

raw patch · 3 files changed

+89/−200 lines, 3 filesdep ~dataframe-coredep ~dataframe-operations

Dependency ranges changed: dataframe-core, dataframe-operations

Files

dataframe-expr-serializer.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.4 name:               dataframe-expr-serializer-version:            1.2.1.0+version:            1.3.0.0 synopsis:           Serialize and deserialize dataframe expressions and pipelines. description:     Save fitted @Expr@ values and whole pipelines (ordered @[NamedExpr]@) to a@@ -39,8 +39,8 @@     build-depends:      base >= 4 && < 5,                         aeson >= 0.11.0.0 && < 3,                         bytestring >= 0.11 && < 0.14,-                        dataframe-core >= 2.5 && < 2.6,-                        dataframe-operations >= 2.5 && < 2.6,+                        dataframe-core >= 2.6 && < 2.7,+                        dataframe-operations >= 2.6 && < 2.7,                         text >= 2.1 && < 3,                         vector >= 0.13 && < 0.15     hs-source-dirs:     src
src/DataFrame/Expr/Serialize.hs view
@@ -5,25 +5,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -{- | Persist dataframe expressions and pipelines, and load them back for-inference in another process (or from Python — see "DataFrame.IR.ExprJson" for-the shared wire format).--  * A single 'Expr' round-trips via 'encodeExpr' / 'decodeExprAny' (or the-    type-asserting 'decodeExprAt') and the file helpers below.-  * A /pipeline/ is an ordered @['NamedExpr']@ — the same shape a fitted-    @Transform@ carries. 'encodeNamedExprs' / 'decodeNamedExprs' wrap it in a-    versioned envelope; replay a decoded pipeline for inference with-    @deriveMany@ / @applyTransform@.--All functions are pure 'Either'; the @save@ helpers report an encode failure-before touching the file (no partial write), and the @load@ helpers turn a-missing/unreadable file into a 'Left' rather than throwing.--Limitations inherited from the wire format: @CastWith@ / @CastExprWith@ (they-hold closures) and @collect@ (its list-typed output has no type tag) cannot be-serialized.--}+-- | Save and load dataframe expressions and pipelines for inference elsewhere. module DataFrame.Expr.Serialize (     -- * Single expressions (re-exported wire codec)     SomeExpr (..),@@ -64,22 +46,20 @@     parseSomeExpr,  ) import DataFrame.Internal.Column (Columnable)-import DataFrame.Internal.Expression (Expr, NamedExpr, UExpr (..))+import DataFrame.Internal.Expression (Expr, NamedExpr)  -- | Decode a single expression from a strict JSON 'BS.ByteString'. decodeExprFromBytes :: BS.ByteString -> Either String SomeExpr decodeExprFromBytes bs = Aeson.eitherDecodeStrict bs >>= decodeExprAny -{- | Encode an ordered pipeline (named output expressions) into the versioned-envelope @{ "version": 1, "outputs": [ { "name", "expr" }, … ] }@.--}+-- | Encode an ordered pipeline of named expressions into a versioned envelope. encodeNamedExprs :: [NamedExpr] -> Either String Aeson.Value encodeNamedExprs nes = do     outs <- traverse encodeOne nes     Right $ object ["version" .= (1 :: Int), "outputs" .= outs]   where     encodeOne :: NamedExpr -> Either String Aeson.Value-    encodeOne (name, UExpr e) = do+    encodeOne (name, SomeExpr e) = do         ev <- encodeExpr e         Right $ object ["name" .= name, "expr" .= ev] @@ -98,8 +78,8 @@     parseOne = Aeson.withObject "PipelineOutput" $ \o -> do         name <- o .: "name" :: Aeson.Parser T.Text         rawExpr <- o .: "expr"-        SomeExpr _ e <- parseSomeExpr rawExpr-        return (name, UExpr e)+        SomeExpr e <- parseSomeExpr rawExpr+        return (name, SomeExpr e)  -- | Encode an expression and write it to a file. No file is written on failure. saveExprToFile :: (Columnable a) => FilePath -> Expr a -> IO (Either String ())@@ -111,9 +91,7 @@ loadExprFromFile :: FilePath -> IO (Either String SomeExpr) loadExprFromFile fp = (>>= decodeExprFromBytes) <$> readBytes fp -{- | Load an expression from a file, asserting its output type. Fails with-'Left' if the stored expression has a different output type.--}+-- | Load an expression from a file, failing if its output type differs. loadExprAtFromFile ::     forall a. (Columnable a) => FilePath -> IO (Either String (Expr a)) loadExprAtFromFile fp = (>>= decode) <$> readBytes fp
src/DataFrame/IR/ExprJson.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-}@@ -7,29 +8,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -{- | JSON wire format for 'Expr' values.--  The Python bindings build expression trees as JSON; this module decodes them-  back into 'Expr' GADTs (typed) and encodes them in the reverse direction-  (used for shipping fitted decision trees back to Python).--  Encoded shape:--  > { "node": "col"   , "out_type": "double", "name": "x" }-  > { "node": "lit"   , "out_type": "int"   , "value": 42 }-  > { "node": "unary" , "out_type": "double", "op": "toDouble"-  >                   , "arg_type": "int"   , "arg": <expr> }-  > { "node": "binary", "out_type": "bool"  , "op": "leq"-  >                   , "arg_type": "double", "lhs": <expr>, "rhs": <expr> }-  > { "node": "if"    , "out_type": "text"  , "cond": <expr>-  >                   , "then": <expr>, "else": <expr> }--  Operator names follow 'binaryName' / 'unaryName' from-  "DataFrame.Internal.Expression". Nullable variants ("nulladd", "nullor", …)-  are accepted on decode and treated as their non-null equivalents — Python-  data crosses the FFI boundary as non-null Arrow buffers, so this is lossless-  for the supported workflow.--}+-- | JSON wire format for 'Expr' values, decoding and encoding. module DataFrame.IR.ExprJson (     SomeExpr (..),     encodeExpr,@@ -57,7 +36,7 @@ import Data.Type.Equality (TestEquality (testEquality), type (:~:) (Refl)) import qualified Data.Vector.Unboxed as VU import Data.Word (Word16, Word32, Word64, Word8)-import Type.Reflection (TypeRep, Typeable, typeRep)+import Type.Reflection (Typeable, typeRep)  import DataFrame.Expression.Operators (     ifThenElse,@@ -76,15 +55,20 @@     AggStrategy (..),     BinaryOp (binaryName),     Expr (..),+    SomeExpr (..),     UnaryOp (unaryName),  ) import qualified DataFrame.Internal.Expression as F--{- | Existential wrapper around a typed expression decoded from JSON.-TODO: mchavinda - Maybe consolidate with UExpr from the main package.--}-data SomeExpr where-    SomeExpr :: (Columnable a) => TypeRep a -> Expr a -> SomeExpr+import DataFrame.Internal.Expression.Reflect (+    Dict (..),+    RealUnbox,+    floatingDict,+    fracDict,+    numDict,+    ordDict,+    realDict,+    realUnboxDict,+ )  -- | Map a Haskell type to its wire-format tag string. typeTagOf :: forall a. (Typeable a) => Maybe T.Text@@ -133,131 +117,63 @@     "string" -> k (Proxy @String)     _ -> fail $ "DataFrame.IR.ExprJson: unknown type tag: " <> T.unpack t --- | Subset that supports 'Ord' (for comparison ops).+-- | A tag whose type has the class evidence @dict@ provides.+withClassTag ::+    forall c r.+    (forall a. (Columnable a) => Maybe (Dict (c a))) ->+    String ->+    T.Text ->+    (forall a. (Columnable a, c a) => Proxy a -> Aeson.Parser r) ->+    Aeson.Parser r+withClassTag dict what t k = withTypeTag t $ \(p :: Proxy a) -> case dict @a of+    Just Dict -> k p+    Nothing ->+        fail $+            "DataFrame.IR.ExprJson: type does not support " <> what <> ": " <> T.unpack t++-- | Types that support 'Ord' (comparison ops). withOrdTypeTag ::     T.Text ->     (forall a. (Columnable a, Ord a) => Proxy a -> Aeson.Parser r) ->     Aeson.Parser r-withOrdTypeTag t k = case t of-    "int" -> k (Proxy @Int)-    "int8" -> k (Proxy @Int8)-    "int16" -> k (Proxy @Int16)-    "int32" -> k (Proxy @Int32)-    "int64" -> k (Proxy @Int64)-    "word" -> k (Proxy @Word)-    "word8" -> k (Proxy @Word8)-    "word16" -> k (Proxy @Word16)-    "word32" -> k (Proxy @Word32)-    "word64" -> k (Proxy @Word64)-    "integer" -> k (Proxy @Integer)-    "double" -> k (Proxy @Double)-    "float" -> k (Proxy @Float)-    "bool" -> k (Proxy @Bool)-    "char" -> k (Proxy @Char)-    "text" -> k (Proxy @T.Text)-    "string" -> k (Proxy @String)-    _ ->-        fail $ "DataFrame.IR.ExprJson: type does not support ordering: " <> T.unpack t+withOrdTypeTag = withClassTag @Ord ordDict "ordering" --- | Subset that supports 'Num' (arithmetic).+-- | Types that support 'Num' (arithmetic). withNumTypeTag ::     T.Text ->     (forall a. (Columnable a, Num a) => Proxy a -> Aeson.Parser r) ->     Aeson.Parser r-withNumTypeTag t k = case t of-    "int" -> k (Proxy @Int)-    "int8" -> k (Proxy @Int8)-    "int16" -> k (Proxy @Int16)-    "int32" -> k (Proxy @Int32)-    "int64" -> k (Proxy @Int64)-    "word" -> k (Proxy @Word)-    "word8" -> k (Proxy @Word8)-    "word16" -> k (Proxy @Word16)-    "word32" -> k (Proxy @Word32)-    "word64" -> k (Proxy @Word64)-    "integer" -> k (Proxy @Integer)-    "double" -> k (Proxy @Double)-    "float" -> k (Proxy @Float)-    _ ->-        fail $ "DataFrame.IR.ExprJson: type does not support arithmetic: " <> T.unpack t+withNumTypeTag = withClassTag @Num numDict "arithmetic" --- | Subset that supports 'Fractional' (division).+-- | Types that support 'Fractional' (division). withFracTypeTag ::     T.Text ->     (forall a. (Columnable a, Fractional a) => Proxy a -> Aeson.Parser r) ->     Aeson.Parser r-withFracTypeTag t k = case t of-    "double" -> k (Proxy @Double)-    "float" -> k (Proxy @Float)-    _ ->-        fail $-            "DataFrame.IR.ExprJson: type does not support fractional division: "-                <> T.unpack t+withFracTypeTag = withClassTag @Fractional fracDict "fractional division" --- | Subset that supports 'Real' (used by toDouble source types).+-- | Types that support 'Real' (toDouble sources). withRealTypeTag ::     T.Text ->     (forall a. (Columnable a, Real a) => Proxy a -> Aeson.Parser r) ->     Aeson.Parser r-withRealTypeTag t k = case t of-    "int" -> k (Proxy @Int)-    "int8" -> k (Proxy @Int8)-    "int16" -> k (Proxy @Int16)-    "int32" -> k (Proxy @Int32)-    "int64" -> k (Proxy @Int64)-    "word" -> k (Proxy @Word)-    "word8" -> k (Proxy @Word8)-    "word16" -> k (Proxy @Word16)-    "word32" -> k (Proxy @Word32)-    "word64" -> k (Proxy @Word64)-    "integer" -> k (Proxy @Integer)-    "double" -> k (Proxy @Double)-    "float" -> k (Proxy @Float)-    _ ->-        fail $-            "DataFrame.IR.ExprJson: type does not support Real (toDouble source): "-                <> T.unpack t+withRealTypeTag = withClassTag @Real realDict "Real (toDouble source)" --- | Subset that supports 'Floating'.+-- | Types that support 'Floating'. withFloatingTypeTag ::     T.Text ->     (forall a. (Columnable a, Floating a) => Proxy a -> Aeson.Parser r) ->     Aeson.Parser r-withFloatingTypeTag t k = case t of-    "double" -> k (Proxy @Double)-    "float" -> k (Proxy @Float)-    _ ->-        fail $ "DataFrame.IR.ExprJson: type does not support Floating: " <> T.unpack t+withFloatingTypeTag = withClassTag @Floating floatingDict "Floating" -{- | Subset that supports both 'Real' and 'VU.Unbox' (used by variance/median).-'Integer' is deliberately omitted — it is 'Real' but not 'Unbox'.--}+-- | Types that support 'Real' and 'VU.Unbox' (variance, median). withRealUnboxTypeTag ::     T.Text ->     (forall a. (Columnable a, Real a, VU.Unbox a) => Proxy a -> Aeson.Parser r) ->     Aeson.Parser r-withRealUnboxTypeTag t k = case t of-    "int" -> k (Proxy @Int)-    "int8" -> k (Proxy @Int8)-    "int16" -> k (Proxy @Int16)-    "int32" -> k (Proxy @Int32)-    "int64" -> k (Proxy @Int64)-    "word" -> k (Proxy @Word)-    "word8" -> k (Proxy @Word8)-    "word16" -> k (Proxy @Word16)-    "word32" -> k (Proxy @Word32)-    "word64" -> k (Proxy @Word64)-    "double" -> k (Proxy @Double)-    "float" -> k (Proxy @Float)-    _ ->-        fail $-            "DataFrame.IR.ExprJson: type does not support Real+Unbox (variance/median): "-                <> T.unpack t+withRealUnboxTypeTag = withClassTag @RealUnbox realUnboxDict "Real+Unbox (variance/median)" -{- | Encode an 'Expr' to a JSON value. Returns 'Left' on unsupported-constructors (Agg, Over, CastWith, CastExprWith) or unsupported operator-names (binaryUdf, unaryUdf, …).--}+-- | Encode an 'Expr' to JSON, failing on unsupported constructors or operators. encodeExpr :: forall a. (Columnable a) => Expr a -> Either String Aeson.Value encodeExpr expr = case expr of     Col name -> do@@ -405,7 +321,7 @@         Left $             "DataFrame.IR.ExprJson.encodeLit: unsupported type: " <> show (typeRep @a) --- | Map a 'binaryName' string to a wire-format op name. Errors on opaque UDF names.+-- | Map a 'binaryName' string to a wire-format op name. recognizeBinary :: T.Text -> Either String T.Text recognizeBinary n = case n of     "add" -> Right "add"@@ -437,7 +353,7 @@             "DataFrame.IR.ExprJson: unsupported binary op (cannot serialize): "                 <> T.unpack other --- | Map a 'unaryName' string to a wire-format op name. Errors on opaque UDF names.+-- | Map a 'unaryName' string to a wire-format op name. recognizeUnary :: T.Text -> Either String T.Text recognizeUnary n = case n of     "not" -> Right "not"@@ -464,9 +380,7 @@             "DataFrame.IR.ExprJson: unsupported unary op (cannot serialize): "                 <> T.unpack other -{- | Decode a JSON value into a 'SomeExpr'. The output type comes from the-@out_type@ field on the root node.--}+-- | Decode a JSON value into a 'SomeExpr' using the root @out_type@. decodeExprAny :: Aeson.Value -> Either String SomeExpr decodeExprAny = Aeson.parseEither parseSomeExpr @@ -474,15 +388,15 @@ decodeExprAt ::     forall a. (Columnable a) => Aeson.Value -> Either String (Expr a) decodeExprAt v = do-    SomeExpr trep expr <- decodeExprAny v-    case testEquality trep (typeRep @a) of+    SomeExpr (expr :: Expr b) <- decodeExprAny v+    case testEquality (typeRep @b) (typeRep @a) of         Just Refl -> Right expr         Nothing ->             Left $                 "DataFrame.IR.ExprJson.decodeExprAt: expected "                     <> show (typeRep @a)                     <> " but got "-                    <> show trep+                    <> show (typeRep @b)  -- | The Aeson.Parser entry point — useful when composing with bigger parsers. parseSomeExpr :: Aeson.Value -> Aeson.Parser SomeExpr@@ -493,12 +407,12 @@         "col" -> do             name <- o .: "name" :: Aeson.Parser T.Text             withTypeTag outType $ \(_ :: Proxy a) ->-                return $ SomeExpr (typeRep @a) (Col @a name)+                return $ SomeExpr (Col @a name)         "lit" -> do             rawVal <- o .: "value"             withTypeTag outType $ \(_ :: Proxy a) -> do                 litVal <- decodeLit @a rawVal-                return $ SomeExpr (typeRep @a) (Lit @a litVal)+                return $ SomeExpr (Lit @a litVal)         "if" -> do             rawCond <- o .: "cond"             rawThen <- o .: "then"@@ -507,7 +421,7 @@             withTypeTag outType $ \(_ :: Proxy a) -> do                 thenE <- parseExprAt @a rawThen                 elseE <- parseExprAt @a rawElse-                return $ SomeExpr (typeRep @a) (ifThenElse cond thenE elseE)+                return $ SomeExpr (ifThenElse cond thenE elseE)         "unary" -> do             op <- o .: "op" :: Aeson.Parser T.Text             argType <- o .: "arg_type" :: Aeson.Parser T.Text@@ -529,20 +443,20 @@             rawArg <- o .: "arg"             withTypeTag outType $ \(_ :: Proxy a) -> do                 inner <- parseExprAt @a rawArg-                return $ SomeExpr (typeRep @a) (F.over names inner)+                return $ SomeExpr (F.over names inner)         other -> fail $ "DataFrame.IR.ExprJson: unknown node kind: " <> T.unpack other  parseExprAt :: forall a. (Columnable a) => Aeson.Value -> Aeson.Parser (Expr a) parseExprAt v = do-    SomeExpr trep expr <- parseSomeExpr v-    case testEquality trep (typeRep @a) of+    SomeExpr (expr :: Expr b) <- parseSomeExpr v+    case testEquality (typeRep @b) (typeRep @a) of         Just Refl -> return expr         Nothing ->             fail $                 "DataFrame.IR.ExprJson: expected "                     <> show (typeRep @a)                     <> " but got "-                    <> show trep+                    <> show (typeRep @b)  decodeLit :: forall a. (Columnable a) => Aeson.Value -> Aeson.Parser a decodeLit v@@ -606,24 +520,24 @@         requireTag outType "bool"         requireTag argType "bool"         arg <- parseExprAt @Bool rawArg-        return $ SomeExpr (typeRep @Bool) (F.not arg)+        return $ SomeExpr (F.not arg)     "negate" -> withNumTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         arg <- parseExprAt @a rawArg-        return $ SomeExpr (typeRep @a) (negate arg)+        return $ SomeExpr (negate arg)     "abs" -> withNumTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         arg <- parseExprAt @a rawArg-        return $ SomeExpr (typeRep @a) (abs arg)+        return $ SomeExpr (abs arg)     "signum" -> withNumTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         arg <- parseExprAt @a rawArg-        return $ SomeExpr (typeRep @a) (signum arg)+        return $ SomeExpr (signum arg)     "toDouble" -> do         requireTag outType "double"         withRealTypeTag argType $ \(_ :: Proxy a) -> do             arg <- parseExprAt @a rawArg-            return $ SomeExpr (typeRep @Double) (F.toDouble arg)+            return $ SomeExpr (F.toDouble arg)     "exp" -> floatingUnary outType argType rawArg exp     "sqrt" -> floatingUnary outType argType rawArg sqrt     "log" -> floatingUnary outType argType rawArg log@@ -649,7 +563,7 @@ floatingUnary outType argType rawArg f = withFloatingTypeTag outType $ \(_ :: Proxy a) -> do     requireSame outType argType     arg <- parseExprAt @a rawArg-    return $ SomeExpr (typeRep @a) (f arg)+    return $ SomeExpr (f arg)  parseBinary ::     T.Text ->@@ -664,72 +578,72 @@         withTypeTag argType $ \(_ :: Proxy a) -> do             l <- parseExprAt @a rawLhs             r <- parseExprAt @a rawRhs-            return $ SomeExpr (typeRep @Bool) (l .==. r)+            return $ SomeExpr (l .==. r)     "neq" -> do         requireTag outType "bool"         withTypeTag argType $ \(_ :: Proxy a) -> do             l <- parseExprAt @a rawLhs             r <- parseExprAt @a rawRhs-            return $ SomeExpr (typeRep @Bool) (l ./=. r)+            return $ SomeExpr (l ./=. r)     "lt" -> do         requireTag outType "bool"         withOrdTypeTag argType $ \(_ :: Proxy a) -> do             l <- parseExprAt @a rawLhs             r <- parseExprAt @a rawRhs-            return $ SomeExpr (typeRep @Bool) (l .<. r)+            return $ SomeExpr (l .<. r)     "leq" -> do         requireTag outType "bool"         withOrdTypeTag argType $ \(_ :: Proxy a) -> do             l <- parseExprAt @a rawLhs             r <- parseExprAt @a rawRhs-            return $ SomeExpr (typeRep @Bool) (l .<=. r)+            return $ SomeExpr (l .<=. r)     "gt" -> do         requireTag outType "bool"         withOrdTypeTag argType $ \(_ :: Proxy a) -> do             l <- parseExprAt @a rawLhs             r <- parseExprAt @a rawRhs-            return $ SomeExpr (typeRep @Bool) (l .>. r)+            return $ SomeExpr (l .>. r)     "geq" -> do         requireTag outType "bool"         withOrdTypeTag argType $ \(_ :: Proxy a) -> do             l <- parseExprAt @a rawLhs             r <- parseExprAt @a rawRhs-            return $ SomeExpr (typeRep @Bool) (l .>=. r)+            return $ SomeExpr (l .>=. r)     "and" -> do         requireTag outType "bool"         l <- parseExprAt @Bool rawLhs         r <- parseExprAt @Bool rawRhs-        return $ SomeExpr (typeRep @Bool) (l .&&. r)+        return $ SomeExpr (l .&&. r)     "or" -> do         requireTag outType "bool"         l <- parseExprAt @Bool rawLhs         r <- parseExprAt @Bool rawRhs-        return $ SomeExpr (typeRep @Bool) (l .||. r)+        return $ SomeExpr (l .||. r)     "add" -> withNumTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         l <- parseExprAt @a rawLhs         r <- parseExprAt @a rawRhs-        return $ SomeExpr (typeRep @a) (l + r)+        return $ SomeExpr (l + r)     "sub" -> withNumTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         l <- parseExprAt @a rawLhs         r <- parseExprAt @a rawRhs-        return $ SomeExpr (typeRep @a) (l - r)+        return $ SomeExpr (l - r)     "mult" -> withNumTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         l <- parseExprAt @a rawLhs         r <- parseExprAt @a rawRhs-        return $ SomeExpr (typeRep @a) (l * r)+        return $ SomeExpr (l * r)     "divide" -> withFracTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         l <- parseExprAt @a rawLhs         r <- parseExprAt @a rawRhs-        return $ SomeExpr (typeRep @a) (l / r)+        return $ SomeExpr (l / r)     "exponentiate" -> withFloatingTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         l <- parseExprAt @a rawLhs         r <- parseExprAt @a rawRhs-        return $ SomeExpr (typeRep @a) (l ** r)+        return $ SomeExpr (l ** r)     "nulladd" -> parseBinary "add" outType argType rawLhs rawRhs     "nullsub" -> parseBinary "sub" outType argType rawLhs rawRhs     "nullmul" -> parseBinary "mult" outType argType rawLhs rawRhs@@ -738,21 +652,18 @@     "nullor" -> parseBinary "or" outType argType rawLhs rawRhs     other -> fail $ "DataFrame.IR.ExprJson: unsupported binary op: " <> T.unpack other -{- | Decode an aggregation node. Dispatches on the stored aggregation name to the-matching 'DataFrame.Functions' builder, picking the type-tag dispatcher demanded-by that builder's constraints.--}+-- | Decode an aggregation node by dispatching on its stored aggregation name. parseAgg :: T.Text -> T.Text -> T.Text -> Aeson.Value -> Aeson.Parser SomeExpr parseAgg aggName outType argType rawArg = case aggName of     "count" -> do         requireTag outType "int"         withTypeTag argType $ \(_ :: Proxy a) -> do             arg <- parseExprAt @a rawArg-            return $ SomeExpr (typeRep @Int) (F.count arg)+            return $ SomeExpr (F.count arg)     "sum" -> withNumTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         arg <- parseExprAt @a rawArg-        return $ SomeExpr (typeRep @a) (F.sum arg)+        return $ SomeExpr (F.sum arg)     "minimum" -> ordAgg F.minimum     "maximum" -> ordAgg F.maximum     "mode" -> ordAgg F.mode@@ -760,7 +671,7 @@         requireTag outType "double"         withRealTypeTag argType $ \(_ :: Proxy a) -> do             arg <- parseExprAt @a rawArg-            return $ SomeExpr (typeRep @Double) (F.mean arg)+            return $ SomeExpr (F.mean arg)     "variance" -> realUnboxAgg F.variance     "median" -> realUnboxAgg F.median     "collect" ->@@ -775,7 +686,7 @@     ordAgg op = withOrdTypeTag outType $ \(_ :: Proxy a) -> do         requireSame outType argType         arg <- parseExprAt @a rawArg-        return $ SomeExpr (typeRep @a) (op arg)+        return $ SomeExpr (op arg)     realUnboxAgg ::         (forall a. (Columnable a, Real a, VU.Unbox a) => Expr a -> Expr Double) ->         Aeson.Parser SomeExpr@@ -783,4 +694,4 @@         requireTag outType "double"         withRealUnboxTypeTag argType $ \(_ :: Proxy a) -> do             arg <- parseExprAt @a rawArg-            return $ SomeExpr (typeRep @Double) (op arg)+            return $ SomeExpr (op arg)