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 +6/−0
- Frames.cabal +22/−2
- demo/MissingData.hs +26/−2
- src/Frames/CSV.hs +15/−6
- src/Frames/CoRec.hs +3/−1
- src/Frames/ColumnUniverse.hs +26/−10
- test/DataCSV.hs +57/−0
- test/Overlap.hs +12/−0
- test/PrettyTH.hs +61/−0
- test/Spec.hs +61/−0
- test/Temp.hs +13/−0
- test/data/employees.csv +3/−0
- test/data/managers.csv +3/−0
- test/examples.toml +286/−0
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)"""