packages feed

Frames 0.1.8 → 0.1.9

raw patch · 14 files changed

+594/−21 lines, 14 filesdep +hspecdep +htomldep +prettydep ~basedep ~pipesdep ~textPVP ok

version bump matches the API change (PVP)

Dependencies added: hspec, htoml, pretty, regex-applicative, temporary, unordered-containers

Dependency ranges changed: base, pipes, text

API changes (from Hackage documentation)

+ Frames.CSV: lowerHead :: Text -> Maybe Text
+ Frames.CoRec: foldCoRec :: (forall a. RElem a ts (RIndex a ts) => f a -> b) -> CoRec f ts -> b
+ Frames.ColumnUniverse: parsedTypeRep :: ColInfo a -> Parsed TypeRep

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+# 0.1.9++Fixed column type inference bug that led the inferencer to prefer `Bool` too strongly.++This was fallout from typing columns whose values are all 0 or 1 as `Bool`.+ # 0.1.6  Re-export `Frames.CSV.declareColumn` from `Frames`. This makes it much
Frames.cabal view
@@ -1,5 +1,5 @@ name:                Frames-version:             0.1.8+version:             0.1.9 synopsis:            Data frames For working with tabular data files description:         User-friendly, type safe, runtime efficient tooling for                      working with tabular data deserialized from@@ -17,6 +17,8 @@ extra-source-files:  benchmarks/*.hs benchmarks/*.py                      demo/Main.hs CHANGELOG.md                      data/GetData.hs+                     test/examples.toml+                     test/data/managers.csv test/data/employees.csv cabal-version:       >=1.10 tested-with:         GHC == 7.10.3, GHC == 8.0.1 @@ -156,7 +158,7 @@     buildable: False   main-is: MissingData.hs   if flag(demos)-    build-depends: base, Frames, vinyl+    build-depends: base, Frames, vinyl, pipes   hs-source-dirs: demo   default-language: Haskell2010 @@ -179,3 +181,21 @@     build-depends: base, Frames, vinyl, text, readable   hs-source-dirs: demo   default-language: Haskell2010++test-suite spec+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  other-modules:       DataCSV PrettyTH Temp+  build-depends:       base, text, hspec, Frames, template-haskell,+                       temporary, directory, htoml, regex-applicative, pretty,+                       unordered-containers+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall+  default-language:    Haskell2010++test-suite overlap+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Overlap.hs+  build-depends:       base, Frames+  default-language:    Haskell2010
demo/MissingData.hs view
@@ -1,11 +1,14 @@-{-# LANGUAGE DataKinds, FlexibleInstances, QuasiQuotes, TypeOperators,-             UndecidableInstances #-}+{-# LANGUAGE DataKinds, FlexibleContexts, FlexibleInstances,+             MultiParamTypeClasses, QuasiQuotes, TemplateHaskell,+             TypeOperators, UndecidableInstances #-} -- | An example of dealing with rows that contain missing data. We may -- want to fill in the gaps with default values. import Data.Monoid ((<>), First(..)) import Data.Vinyl (Rec(..), rmap, RecApplicative, rapply) import Data.Vinyl.Functor (Lift(..)) import Frames hiding ((:&))+import Pipes (cat, Producer, (>->))+import Pipes.Prelude as P  -- An en passant Default class class Default a where@@ -42,6 +45,27 @@ -- We can fill in the holes with our default record. unholy :: Maybe (Record '[MyString, MyInt, MyBool]) unholy = recMaybe . rmap getFirst $ rapply (rmap (Lift . flip (<>)) def) holyRow++-- * Reading a CSV file with missing data++instance Default ("col_a" :-> Int) where def = Col 0+instance Default ("col_b" :-> Text) where def = Col mempty++tableTypes "Row" "data/missing.csv"++-- | Fill in missing columns with a default 'Row' value synthesized+-- from 'Default' instances.+holesFilled :: Producer Row IO ()+holesFilled = readTableMaybe "data/missing.csv" >-> P.map (fromJust . holeFiller)+  where holeFiller :: Rec Maybe (RecordColumns Row) -> Maybe Row+        holeFiller = recMaybe+                   . rmap getFirst+                   . rapply (rmap (Lift . flip (<>)) def)+                   . rmap First+        fromJust = maybe (error "Frames holesFilled failure") id++showFilledHoles :: IO ()+showFilledHoles = pipePreview holesFilled 10 cat  main :: IO () main = return ()
src/Frames/CSV.hs view
@@ -27,7 +27,7 @@ import Control.Monad (MonadPlus(..), when, void) import Control.Monad.IO.Class import Data.Char (isAlpha, isAlphaNum, toLower, toUpper)-import Data.Maybe (isNothing)+import Data.Maybe (isNothing, fromMaybe) import Data.Monoid ((<>)) import Data.Proxy import qualified Data.Text as T@@ -271,7 +271,8 @@ mkColTDec :: TypeQ -> Name -> DecQ mkColTDec colTypeQ colTName = tySynD colTName [] colTypeQ --- | Declare a singleton value of the given column type.+-- | Declare a singleton value of the given column type and lenses for+-- working with that column. mkColPDec :: Name -> TypeQ -> T.Text -> DecsQ mkColPDec colTName colTy colPName = sequenceA [tySig, val, tySig', val']   where nm = mkName $ T.unpack colPName@@ -296,15 +297,17 @@                     (normalB [e|rlens' (Proxy :: Proxy $(conT colTName))|])                     [] +lowerHead :: T.Text -> Maybe T.Text+lowerHead = fmap aux . T.uncons+  where aux (c,t) = T.cons (toLower c) t+ -- | For each column, we declare a type synonym for its type, and a -- Proxy value of that type. colDec :: ColumnTypeable a => T.Text -> T.Text -> a -> DecsQ colDec prefix colName colTy = (:) <$> mkColTDec colTypeQ colTName'                                   <*> mkColPDec colTName' colTyQ colPName   where colTName = sanitizeTypeName (prefix <> colName)-        colPName = maybe "colDec impossible"-                         (\(c,t) -> T.cons (toLower c) t)-                         (T.uncons colTName)+        colPName = fromMaybe "colDec impossible" (lowerHead colTName)         colTName' = mkName $ T.unpack colTName         colTyQ = colType colTy         colTypeQ = [t|$(litT . strTyLit $ T.unpack colName) :-> $colTyQ|]@@ -424,7 +427,7 @@                       h:t -> mkName $ toLower h : t ++ "Parser"      optsTy <- sigD optsName [t|ParserOptions|]      optsDec <- valD (varP optsName) (normalB $ lift opts) []-     colDecs <- concat <$> mapM (uncurry $ colDec (T.pack tablePrefix)) headers+     colDecs <- concat <$> mapM (uncurry mkColDecs) headers      return (recTy : optsTy : optsDec : colDecs)      -- (:) <$> (tySynD (mkName n) [] (recDec' headers))      --     <*> (concat <$> mapM (uncurry $ colDec (T.pack prefix)) headers)@@ -432,3 +435,9 @@         colNames' | null columnNames = Nothing                   | otherwise = Just (map T.pack columnNames)         opts = ParserOptions colNames' separator (RFC4180Quoting '\"')+        mkColDecs colNm colTy = do+          let safeName = tablePrefix ++ (T.unpack . sanitizeTypeName $ colNm)+          mColNm <- lookupTypeName safeName+          case mColNm of+            Just _ -> pure []+            Nothing -> colDec (T.pack tablePrefix) colNm colTy
src/Frames/CoRec.hs view
@@ -38,6 +38,9 @@ data CoRec :: (* -> *) -> [*] -> * where   Col :: RElem a ts (RIndex a ts) => !(f a) -> CoRec f ts +foldCoRec :: (forall a. RElem a ts (RIndex a ts) => f a -> b) -> CoRec f ts -> b+foldCoRec f (Col x) = f x+ -- | A Field of a 'Record' is a 'CoRec Identity'. type Field = CoRec Identity @@ -72,7 +75,6 @@ zipRecsWith :: (forall a. f a -> g a -> h a) -> Rec f as -> Rec g as -> Rec h as zipRecsWith _ RNil      _         = RNil zipRecsWith f (r :& rs) (s :& ss) = f r s :& zipRecsWith f rs ss-  -- | Remove a 'Dict' wrapper from a value. dictId :: Dict c a -> Identity a
src/Frames/ColumnUniverse.hs view
@@ -18,7 +18,8 @@              TypeOperators,              UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}-module Frames.ColumnUniverse (CoRec, Columns, ColumnUniverse, CommonColumns) where+module Frames.ColumnUniverse (CoRec, Columns, ColumnUniverse, CommonColumns, +                              parsedTypeRep) where import Language.Haskell.TH #if __GLASGOW_HASKELL__ < 800 import Data.Monoid@@ -88,6 +89,9 @@ -- types. newtype ColInfo a = ColInfo (Q Type, Parsed (Typed a)) +parsedTypeRep :: ColInfo a -> Parsed TypeRep+parsedTypeRep (ColInfo (_,p)) = fmap getConst p+ -- | We use a join semi-lattice on types for representations. The -- bottom of the lattice is effectively an error (we have nothing to -- represent), @Bool < Int@, @Int < Double@, and @forall n. n <= Text@.@@ -109,10 +113,12 @@   | otherwise = Nothing lubTypeReps (Definitely trX) (Definitely trY)   | trX == trY = Just EQ-  | trX == trInt && trY == trDbl = Just LT-  | trX == trDbl && trY == trInt = Just GT+  | trX == trInt  && trY == trDbl = Just LT+  | trX == trDbl  && trY == trInt = Just GT   | trX == trBool && trY == trInt = Just LT-  | trX == trInt && trY == trBool = Just GT+  | trX == trInt  && trY == trBool = Just GT+  | trX == trBool && trY == trDbl = Just LT+  | trX == trDbl  && trY == trBool = Just GT   | otherwise = Nothing   where trInt = typeRep (Proxy :: Proxy Int)         trDbl = typeRep (Proxy :: Proxy Double)@@ -128,16 +134,26 @@         Nothing -> mempty  -- | Find the best (i.e. smallest) 'CoRec' variant to represent a--- parsed value.+-- parsed value. For inspection in GHCi after loading this module,+-- consider this example:+-- +-- >>> :set -XTypeApplications+-- >>> :set -XOverloadedStrings+-- >>> import Frames.CoRec (foldCoRec)+-- >>> foldCoRec parsedTypeRep (bestRep @CommonColumns "2.3")+-- Definitely Double bestRep :: forall ts.            (LAll Parseable ts, LAll Typeable ts, FoldRec ts ts,             RecApplicative ts, T.Text ∈ ts)         => T.Text -> CoRec ColInfo ts-bestRep = aux-        . fromMaybe (Col (Compose $ Possibly (mkTyped :: Typed T.Text)))-        . firstField-        . elementTypes-        . (tryParseAll :: T.Text -> Rec (Maybe :. (Parsed :. Proxy)) ts)+bestRep t+  | T.null t = aux (Col (Compose (Possibly (mkTyped :: Typed T.Text))))+  | otherwise = aux+              . fromMaybe (Col (Compose $ Possibly (mkTyped :: Typed T.Text)))+              . firstField+              . elementTypes+              . (tryParseAll :: T.Text -> Rec (Maybe :. (Parsed :. Proxy)) ts)+              $ t   where aux :: CoRec (Parsed :. Typed) ts -> CoRec ColInfo ts         aux (Col (Compose d@(Possibly (Const tr)))) =           Col (ColInfo (quoteType tr, d))
+ test/DataCSV.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}+module DataCSV where+import Control.Monad ((>=>))+import Data.Bifunctor (first)+import qualified Data.HashMap.Lazy as H+import Data.Maybe (catMaybes)+import qualified Data.Text as T+import qualified Data.Text.IO as T+import Language.Haskell.TH.Syntax (Lift(..))+import Text.Toml+import Text.Toml.Types (Node (VTable, VString), Table)++managersCsv :: [Char]+managersCsv = "id,manager,age,pay\n\+               \0,Joe,53,\"80,000\"\n\+               \1,Sarah,44,\"80,000\""++employeesCsv :: [Char]+employeesCsv = "id,employee,age,pay,manager_id\n\+                \2,Sadie,28,\"40,000\",0\n\+                \3,Tom,25,\"40,000\",1"++data CsvExample = CsvExample { name :: String, csv :: String, generated :: String }++instance Lift CsvExample where+  lift (CsvExample n c g) = [e| CsvExample n c g |]++examplesFrom :: FilePath -> IO [CsvExample]+examplesFrom fp = (either error id . ((first show . parseTomlDoc "examples") >=> go))+                <$> T.readFile fp+  where go :: Table -> Either String [CsvExample]+        go = fmap catMaybes . mapM (uncurry ex . first T.unpack) . H.toList+        ex :: String -> Node -> Either String (Maybe CsvExample)+        ex k (VTable v) =+          do c <- case H.lookup "csv" v of+                    Nothing -> Right Nothing -- ("No csv key in "++k)+                    Just (VString c) -> Right (Just (T.unpack c))+                    Just _ -> Left ("csv key not a string in " ++ k)+             g <- case H.lookup "generated" v of+                    Nothing -> Left ("No generated key in " ++ k)+                    Just (VString g) -> Right (Just (T.unpack g))+                    Just _ -> Left ("generated key not a string in " ++ k)+             return (CsvExample k <$> c <*> g)+        ex k _ = Left (k ++ " is not a table")++generatedFrom :: FilePath -> String -> IO String+generatedFrom fp key = (either error id . (>>= go)+                        . first show . parseTomlDoc "examples")+                       <$> T.readFile fp+  where go :: Table -> Either String String+        go toml = do tbl <- case H.lookup (T.pack key) toml of+                              Just (VTable t) -> Right t+                              _ -> Left (key ++ " is not a table")+                     case H.lookup "generated" tbl of+                       Just (VString g) -> Right (T.unpack g)+                       Just _ -> Left ("generated key not a string in " ++ key)+                       Nothing -> Left ("No generated key in " ++ key)
+ test/Overlap.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE DataKinds, FlexibleContexts, TemplateHaskell #-}+module Main (main) where+import Frames++-- These data files have overlapping column definitions. Frames should+-- not try to re-define an existing identifier.++tableTypes "ManagerRec" "test/data/managers.csv"+tableTypes "EmployeeRec" "test/data/employees.csv"++main :: IO ()+main = return ()
+ test/PrettyTH.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings #-}+module PrettyTH where+import Data.Char (isSpace)+import Frames+import Language.Haskell.TH+import Language.Haskell.TH.PprLib+import Text.PrettyPrint+import Text.Regex.Applicative++import Temp++generateCode :: String -> String -> Q Exp+generateCode rowName txt =+    withTempContents+      txt+      (\fp -> tableTypes rowName fp+              >>= stringE+              . makePretty+              . renderStyle (style {ribbonsPerLine=1.0, lineLength=150})+              . to_HPJ_Doc . ppr)++-- | Make template haskell-generated code more readable by+-- unqualifying common names, making type operators infix, erasing+-- inferrable types, and adding a bit of whitespace.+makePretty :: String -> String+makePretty = -- Add new lines before type synonym definitions+             replace' "\ntype " "\n\ntype "+             -- Make :-> and ': type operators infix+             . (!! 10) . iterate (replace infixCons)+             . replace infixNil+             . replace infixCol+               -- Erase inferrable type+             . replace ((\x y -> x ++ " ∈ " ++ y)+                        <$> ("RElem " *> some (psym (not . isSpace)))+                        <*> ((some (psym isSpace) *> some (psym (not . isSpace)))+                             <* " (RIndex " <* some (psym (/= ')')) <* ")"))+               -- Unqualify names+             . replace' "Frames.CSV.ParserOptions" "ParserOptions"+             . replace' "GHC.Base." ""+             . replace' "GHC.Types." ""+             . replace' "Data.Vinyl.Core." ""+             . replace' "Data.Vinyl.Lens." ""+             . replace' "Data.Vinyl.TypeLevel." ""+             . replace' "Frames.Rec." ""+             . replace' "Frames.RecLens." ""+             . replace' "Data.Proxy." ""+             . replace' "Data.Text." "T."+             . replace' "Data.Text.Internal." "T."+             . replace' "'GHC.Types.:" "(':)"+  where replace' orig replacement = replace (replacement <$ string orig)+        infixCol = (\x y -> '"' : x ++ "\" :-> " ++ y)+                   <$> ("Frames.Col.:-> \"" *> (some (psym (/= '"'))) <* "\"" +                        <* some (psym isSpace))+                   <*> some (psym (/= ' '))+        infixNil = (\x -> '[' : x ++ "]")+                   <$> ("((':) (" *> some (psym (/= ')')) <* ")" +                        <* some (psym isSpace) <* "'[])")+        infixCons = (\x y -> '[' : x ++ ", " ++ y ++ "]")+                    <$> ("((':) (" *> some (psym (/= ')')) <* ")"+                         <* some (psym isSpace) <* "[")+                    <*> (some (psym (/= ']')) <* "])")
+ test/Spec.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE CPP, TemplateHaskell, QuasiQuotes #-}+module Main (manualGeneration, main) where+import Data.List (find)+import Data.Monoid (First(..))+import Language.Haskell.TH as TH+import Language.Haskell.TH.Syntax (addDependentFile)+import Test.Hspec as H+import Frames+import DataCSV+import PrettyTH++-- | Extract all example @(CSV, generatedCode)@ pairs from+-- @test/examples.toml@+csvTests :: [(CsvExample, String)]+csvTests = $(do addDependentFile "test/examples.toml"+                csvExamples <- TH.runIO (examplesFrom "test/examples.toml")+                ListE <$> mapM (\x@(CsvExample _ c _) -> +                                  [e|(x,$(generateCode "Row" c))|])+                               csvExamples)++-- | Detect type-compatible re-used names and do not attempt to+-- re-generate definitions for them. This does not do the right thing+-- since the generated declarations are never turned into actual+-- declarations, they do not affect the `lookupTypeName` call that is+-- designed to prevent duplicate declarations.+overlappingGeneration :: String+overlappingGeneration = m ++ "\n\n" ++ e+  where m = $(do csvExamples <- TH.runIO (examplesFrom "test/examples.toml")+                 let Just (CsvExample _ managers _) = +                       find (\(CsvExample k _ _) -> k == "managers") csvExamples+                 generateCode "ManagerRec" managers)+        e = $(do csvExamples <- TH.runIO (examplesFrom "test/examples.toml")+                 let Just (CsvExample _ employees _) = +                       find (\(CsvExample k _ _) -> k == "employees") csvExamples+                 generateCode "EmployeeRec" employees)++-- | To generate example generated code from raw CSV data, add the csv+-- to @examples.toml@ and set the @generated@ key to an empty+-- string. Then load this file into a REPL that has @:set+-- -XTemplateHaskell@ and evaluate @putStrLn $(manualGeneration+-- "employees")@, for example.+-- +-- Note that to load this file into a REPL may require some fiddling+-- with the path to the examples file in the 'csvTests' splice above.+manualGeneration :: String -> Q Exp+manualGeneration k = do csvExamples <- TH.runIO (examplesFrom "test/examples.toml")+                        maybe (error ("Table " ++ k ++ " not found")) +                              (generateCode "Row") +                              (getFirst $ foldMap aux csvExamples)+  where aux :: CsvExample -> First String+        aux (CsvExample k' c _) = if k == k' then pure c else mempty++main :: IO ()+main = do+  hspec $+    do describe "Haskell type generation" $ +         mapM_ (\(CsvExample k _ g, g') -> it k (g' `shouldBe` g)) csvTests+       describe "Multiple tables" $+          do g <- H.runIO $ +                  generatedFrom "test/examples.toml" "managers_employees"+             it "Shouldn't duplicate columns" pending
+ test/Temp.hs view
@@ -0,0 +1,13 @@+module Temp where+import Language.Haskell.TH+import System.Directory+import System.IO (hClose)+import System.IO.Temp (openTempFile)++withTempContents :: String -> (FilePath -> Q r) -> Q r+withTempContents contents f =+  do fp <- runIO $ do dir <- getTemporaryDirectory  +                      (fp,h) <- openTempFile dir "FramesSpec"+                      hClose h+                      fp <$ writeFile fp contents+     f fp <* runIO (removeFile fp)
+ test/data/employees.csv view
@@ -0,0 +1,3 @@+id,employee,age,pay,manager_id+3,Sadie,28,"40,000",1+4,Tom,25,"40,000",2
+ test/data/managers.csv view
@@ -0,0 +1,3 @@+id,manager,age,pay+1,Joe,53,"80,000"+2,Sarah,44,"80,000"
+ test/examples.toml view
@@ -0,0 +1,286 @@+[managers]+csv = """+id,manager,age,pay+1,Joe,53,"80,000"+2,Sarah,44,"80,000"+"""++generated = """+type Row = Record ["id" :-> Int, "manager" :-> T.Text, "age" :-> Int, "pay" :-> Double]+rowParser :: ParserOptions+rowParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')++type Id = "id" :-> Int+id :: forall f_0 rs_1 . (Functor f_0, Id ∈ rs_1) =>+                        (Int -> f_0 Int) -> Record rs_1 -> f_0 (Record rs_1)+id = rlens (Proxy :: Proxy Id)+id' :: forall f_2 g_3 rs_4 . (Functor f_2, Functor g_3, Id ∈ rs_4) =>+                             (g_3 Id -> f_2 (g_3 Id)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)+id' = rlens' (Proxy :: Proxy Id)++type Manager = "manager" :-> T.Text+manager :: forall f_5 rs_6 . (Functor f_5, Manager ∈ rs_6) =>+                             (T.Text -> f_5 T.Text) -> Record rs_6 -> f_5 (Record rs_6)+manager = rlens (Proxy :: Proxy Manager)+manager' :: forall f_7 g_8 rs_9 . (Functor f_7,+                                   Functor g_8,+                                   Manager ∈ rs_9) =>+                                  (g_8 Manager -> f_7 (g_8 Manager)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)+manager' = rlens' (Proxy :: Proxy Manager)++type Age = "age" :-> Int+age :: forall f_10 rs_11 . (Functor f_10, Age ∈ rs_11) =>+                           (Int -> f_10 Int) -> Record rs_11 -> f_10 (Record rs_11)+age = rlens (Proxy :: Proxy Age)+age' :: forall f_12 g_13 rs_14 . (Functor f_12,+                                  Functor g_13,+                                  Age ∈ rs_14) =>+                                 (g_13 Age -> f_12 (g_13 Age)) -> Rec g_13 rs_14 -> f_12 (Rec g_13 rs_14)+age' = rlens' (Proxy :: Proxy Age)++type Pay = "pay" :-> Double+pay :: forall f_15 rs_16 . (Functor f_15, Pay ∈ rs_16) =>+                           (Double -> f_15 Double) -> Record rs_16 -> f_15 (Record rs_16)+pay = rlens (Proxy :: Proxy Pay)+pay' :: forall f_17 g_18 rs_19 . (Functor f_17,+                                  Functor g_18,+                                  Pay ∈ rs_19) =>+                                 (g_18 Pay -> f_17 (g_18 Pay)) -> Rec g_18 rs_19 -> f_17 (Rec g_18 rs_19)+pay' = rlens' (Proxy :: Proxy Pay)"""++[employees]+csv = """+id,employee,age,pay,manager_id+3,Sadie,28,"40,000",1+4,Tom,25,"40,000",2+"""++generated = """+type Row = Record ["id" :-> Int, "employee" :-> T.Text, "age" :-> Int, "pay" :-> Double, "manager_id" :-> Int]+rowParser :: ParserOptions+rowParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')++type Id = "id" :-> Int+id :: forall f_0 rs_1 . (Functor f_0, Id ∈ rs_1) =>+                        (Int -> f_0 Int) -> Record rs_1 -> f_0 (Record rs_1)+id = rlens (Proxy :: Proxy Id)+id' :: forall f_2 g_3 rs_4 . (Functor f_2, Functor g_3, Id ∈ rs_4) =>+                             (g_3 Id -> f_2 (g_3 Id)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)+id' = rlens' (Proxy :: Proxy Id)++type Employee = "employee" :-> T.Text+employee :: forall f_5 rs_6 . (Functor f_5, Employee ∈ rs_6) =>+                              (T.Text -> f_5 T.Text) -> Record rs_6 -> f_5 (Record rs_6)+employee = rlens (Proxy :: Proxy Employee)+employee' :: forall f_7 g_8 rs_9 . (Functor f_7,+                                    Functor g_8,+                                    Employee ∈ rs_9) =>+                                   (g_8 Employee -> f_7 (g_8 Employee)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)+employee' = rlens' (Proxy :: Proxy Employee)++type Age = "age" :-> Int+age :: forall f_10 rs_11 . (Functor f_10, Age ∈ rs_11) =>+                           (Int -> f_10 Int) -> Record rs_11 -> f_10 (Record rs_11)+age = rlens (Proxy :: Proxy Age)+age' :: forall f_12 g_13 rs_14 . (Functor f_12,+                                  Functor g_13,+                                  Age ∈ rs_14) =>+                                 (g_13 Age -> f_12 (g_13 Age)) -> Rec g_13 rs_14 -> f_12 (Rec g_13 rs_14)+age' = rlens' (Proxy :: Proxy Age)++type Pay = "pay" :-> Double+pay :: forall f_15 rs_16 . (Functor f_15, Pay ∈ rs_16) =>+                           (Double -> f_15 Double) -> Record rs_16 -> f_15 (Record rs_16)+pay = rlens (Proxy :: Proxy Pay)+pay' :: forall f_17 g_18 rs_19 . (Functor f_17,+                                  Functor g_18,+                                  Pay ∈ rs_19) =>+                                 (g_18 Pay -> f_17 (g_18 Pay)) -> Rec g_18 rs_19 -> f_17 (Rec g_18 rs_19)+pay' = rlens' (Proxy :: Proxy Pay)++type ManagerId = "manager_id" :-> Int+managerId :: forall f_20 rs_21 . (Functor f_20, ManagerId ∈ rs_21) =>+                                 (Int -> f_20 Int) -> Record rs_21 -> f_20 (Record rs_21)+managerId = rlens (Proxy :: Proxy ManagerId)+managerId' :: forall f_22 g_23 rs_24 . (Functor f_22,+                                        Functor g_23,+                                        ManagerId ∈ rs_24) =>+                                       (g_23 ManagerId -> f_22 (g_23 ManagerId)) ->+                                       Rec g_23 rs_24 -> f_22 (Rec g_23 rs_24)+managerId' = rlens' (Proxy :: Proxy ManagerId)"""++[managers_employees]+generated = """+type ManagerRec = Record ["id" :-> Int, "manager" :-> T.Text, "age" :-> Int, "pay" :-> Double]+managerRecParser :: ParserOptions+managerRecParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')++type Id = "id" :-> Int+id :: forall f_0 rs_1 . (Functor f_0, Id ∈ rs_1) =>+                        (Int -> f_0 Bool) -> Record rs_1 -> f_0 (Record rs_1)+id = rlens (Proxy :: Proxy Id)+id' :: forall f_2 g_3 rs_4 . (Functor f_2, Functor g_3, Id ∈ rs_4) =>+                             (g_3 Id -> f_2 (g_3 Id)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)+id' = rlens' (Proxy :: Proxy Id)++type Manager = "manager" :-> T.Text+manager :: forall f_5 rs_6 . (Functor f_5, Manager ∈ rs_6) =>+                             (T.Text -> f_5 T.Text) -> Record rs_6 -> f_5 (Record rs_6)+manager = rlens (Proxy :: Proxy Manager)+manager' :: forall f_7 g_8 rs_9 . (Functor f_7,+                                   Functor g_8,+                                   Manager ∈ rs_9) =>+                                  (g_8 Manager -> f_7 (g_8 Manager)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)+manager' = rlens' (Proxy :: Proxy Manager)++type Age = "age" :-> Int+age :: forall f_10 rs_11 . (Functor f_10, Age ∈ rs_11) =>+                           (Int -> f_10 Int) -> Record rs_11 -> f_10 (Record rs_11)+age = rlens (Proxy :: Proxy Age)+age' :: forall f_12 g_13 rs_14 . (Functor f_12,+                                  Functor g_13,+                                  Age ∈ rs_14) =>+                                 (g_13 Age -> f_12 (g_13 Age)) -> Rec g_13 rs_14 -> f_12 (Rec g_13 rs_14)+age' = rlens' (Proxy :: Proxy Age)++type Pay = "pay" :-> Double+pay :: forall f_15 rs_16 . (Functor f_15, Pay ∈ rs_16) =>+                           (Double -> f_15 Double) -> Record rs_16 -> f_15 (Record rs_16)+pay = rlens (Proxy :: Proxy Pay)+pay' :: forall f_17 g_18 rs_19 . (Functor f_17,+                                  Functor g_18,+                                  Pay ∈ rs_19) =>+                                 (g_18 Pay -> f_17 (g_18 Pay)) -> Rec g_18 rs_19 -> f_17 (Rec g_18 rs_19)+pay' = rlens' (Proxy :: Proxy Pay)++type EmployeeRec = Record ["id" :-> Int, "employee" :-> T.Text, "age" :-> Int, "pay" :-> Double, "manager_id" :-> Int]+employeeRecParser :: ParserOptions+employeeRecParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')++type Employee = "employee" :-> T.Text+employee :: forall f_5 rs_6 . (Functor f_5, Employee ∈ rs_6) =>+                              (T.Text -> f_5 T.Text) -> Record rs_6 -> f_5 (Record rs_6)+employee = rlens (Proxy :: Proxy Employee)+employee' :: forall f_7 g_8 rs_9 . (Functor f_7,+                                    Functor g_8,+                                    Employee ∈ rs_9) =>+                                   (g_8 Employee -> f_7 (g_8 Employee)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)+employee' = rlens' (Proxy :: Proxy Employee)++type Age = "age" :-> Int++type ManagerId = "manager_id" :-> Int+managerId :: forall f_20 rs_21 . (Functor f_20, ManagerId ∈ rs_21) =>+                                 (Int -> f_20 Int) -> Record rs_21 -> f_20 (Record rs_21)+managerId = rlens (Proxy :: Proxy ManagerId)+managerId' :: forall f_22 g_23 rs_24 . (Functor f_22,+                                        Functor g_23,+                                        ManagerId ∈ rs_24) =>+                                       (g_23 ManagerId -> f_22 (g_23 ManagerId)) ->+                                       Rec g_23 rs_24 -> f_22 (Rec g_23 rs_24)+managerId' = rlens' (Proxy :: Proxy ManagerId)+"""++[double_gt_bool]+csv = """+col_a,col_b,col_c+1,9,"1,000,000.00"+2,8,"300,000.00"+3,7,"0"+4,6,0+"""++generated = """+type Row = Record ["col_a" :-> Int, "col_b" :-> Int, "col_c" :-> Double]+rowParser :: ParserOptions+rowParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')++type ColA = "col_a" :-> Int+colA :: forall f_0 rs_1 . (Functor f_0, ColA ∈ rs_1) =>+                          (Int -> f_0 Int) -> Record rs_1 -> f_0 (Record rs_1)+colA = rlens (Proxy :: Proxy ColA)+colA' :: forall f_2 g_3 rs_4 . (Functor f_2,+                                Functor g_3,+                                ColA ∈ rs_4) =>+                               (g_3 ColA -> f_2 (g_3 ColA)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)+colA' = rlens' (Proxy :: Proxy ColA)++type ColB = "col_b" :-> Int+colB :: forall f_5 rs_6 . (Functor f_5, ColB ∈ rs_6) =>+                          (Int -> f_5 Int) -> Record rs_6 -> f_5 (Record rs_6)+colB = rlens (Proxy :: Proxy ColB)+colB' :: forall f_7 g_8 rs_9 . (Functor f_7,+                                Functor g_8,+                                ColB ∈ rs_9) =>+                               (g_8 ColB -> f_7 (g_8 ColB)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)+colB' = rlens' (Proxy :: Proxy ColB)++type ColC = "col_c" :-> Double+colC :: forall f_10 rs_11 . (Functor f_10, ColC ∈ rs_11) =>+                            (Double -> f_10 Double) -> Record rs_11 -> f_10 (Record rs_11)+colC = rlens (Proxy :: Proxy ColC)+colC' :: forall f_12 g_13 rs_14 . (Functor f_12,+                                   Functor g_13,+                                   ColC ∈ rs_14) =>+                                  (g_13 ColC -> f_12 (g_13 ColC)) -> Rec g_13 rs_14 -> f_12 (Rec g_13 rs_14)+colC' = rlens' (Proxy :: Proxy ColC)"""++[text_gt_bool]+csv = """+col_a+"0"+"0"+"0"+"0"+A+"""++generated = """+type Row = Record ["col_a" :-> T.Text]+rowParser :: ParserOptions+rowParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')++type ColA = "col_a" :-> T.Text+colA :: forall f_0 rs_1 . (Functor f_0, ColA ∈ rs_1) =>+                          (T.Text -> f_0 T.Text) -> Record rs_1 -> f_0 (Record rs_1)+colA = rlens (Proxy :: Proxy ColA)+colA' :: forall f_2 g_3 rs_4 . (Functor f_2,+                                Functor g_3,+                                ColA ∈ rs_4) =>+                               (g_3 ColA -> f_2 (g_3 ColA)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)+colA' = rlens' (Proxy :: Proxy ColA)"""++[missing_data]+csv = """+col_a,col_b+"0","x"+"2","x"+"1","x"+,"x"+"0","x"+"""++generated = """+type Row = Record ["col_a" :-> Int, "col_b" :-> T.Text]+rowParser :: ParserOptions+rowParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')++type ColA = "col_a" :-> Int+colA :: forall f_0 rs_1 . (Functor f_0, ColA ∈ rs_1) =>+                          (Int -> f_0 Int) -> Record rs_1 -> f_0 (Record rs_1)+colA = rlens (Proxy :: Proxy ColA)+colA' :: forall f_2 g_3 rs_4 . (Functor f_2,+                                Functor g_3,+                                ColA ∈ rs_4) =>+                               (g_3 ColA -> f_2 (g_3 ColA)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)+colA' = rlens' (Proxy :: Proxy ColA)++type ColB = "col_b" :-> T.Text+colB :: forall f_5 rs_6 . (Functor f_5, ColB ∈ rs_6) =>+                          (T.Text -> f_5 T.Text) -> Record rs_6 -> f_5 (Record rs_6)+colB = rlens (Proxy :: Proxy ColB)+colB' :: forall f_7 g_8 rs_9 . (Functor f_7,+                                Functor g_8,+                                ColB ∈ rs_9) =>+                               (g_8 ColB -> f_7 (g_8 ColB)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)+colB' = rlens' (Proxy :: Proxy ColB)"""