Frames 0.7.0 → 0.7.1
raw patch · 6 files changed
+88/−15 lines, 6 filesdep ~basedep ~ghc-primPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base, ghc-prim
API changes (from Hackage documentation)
- Frames: data Rec (a :: u -> Type) (b :: [u]) :: forall u. () => u -> Type -> [u] -> Type
- Frames.ColumnUniverse: data CoRec (a :: k -> Type) (b :: [k]) :: forall k. () => k -> Type -> [k] -> Type
- Frames.ExtraInstances: instance forall a (f :: a -> *) (r :: a) (rs :: [a]). (Control.DeepSeq.NFData (f r), Control.DeepSeq.NFData (Data.Vinyl.Core.Rec f rs)) => Control.DeepSeq.NFData (Data.Vinyl.Core.Rec f (r : rs))
- Frames.ExtraInstances: instance forall k l (f :: l -> *) (g :: k -> l) (a :: k). Control.DeepSeq.NFData (f (g a)) => Control.DeepSeq.NFData (Data.Vinyl.Functor.Compose f g a)
+ Frames: data Rec (a :: u -> Type) (b :: [u])
+ Frames.ColumnUniverse: data CoRec (a :: k -> Type) (b :: [k])
+ Frames.Exploration: dropRows :: Int -> Frame (Record rs) -> Frame (Record rs)
+ Frames.Exploration: printFrame :: forall rs. (ColumnHeaders rs, RecMapMethod Show ElField rs, RecordToList rs) => String -> Frame (Record rs) -> IO ()
+ Frames.Exploration: showFrame :: forall rs. (ColumnHeaders rs, RecMapMethod Show ElField rs, RecordToList rs) => String -> Frame (Record rs) -> String
+ Frames.Exploration: takeRows :: Int -> Frame (Record rs) -> Frame (Record rs)
+ Frames.ExtraInstances: instance forall l k (f :: l -> *) (g :: k -> l) (a :: k). Control.DeepSeq.NFData (f (g a)) => Control.DeepSeq.NFData (Data.Vinyl.Functor.Compose f g a)
- Frames: (<+>) :: () => Rec f as -> Rec f bs -> Rec f (as ++ bs)
+ Frames: (<+>) :: forall k (f :: k -> Type) (as :: [k]) (bs :: [k]). Rec f as -> Rec f bs -> Rec f (as ++ bs)
- Frames: melt :: forall vs ts ss proxy. (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, HasLength vs, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> FrameRec ts -> FrameRec (ss ++ '["value" :-> CoRec ElField vs])
+ Frames: melt :: forall vs ts ss proxy. (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, HasLength vs, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> FrameRec ts -> FrameRec (ss ++ '["value" :-> CoRec ElField vs])
- Frames: meltRow :: (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> Record ts -> [Record (ss ++ '["value" :-> CoRec ElField vs])]
+ Frames: meltRow :: (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> Record ts -> [Record (ss ++ '["value" :-> CoRec ElField vs])]
- Frames: onCoRec :: RPureConstrained c ts => (forall (a :: k2). (a ∈ ts, c a) => f a -> g b) -> CoRec f ts -> g b
+ Frames: onCoRec :: forall k1 k2 c f (ts :: [k1]) (b :: k2) g. RPureConstrained c ts => (forall (a :: k1). (a ∈ ts, c a) => f a -> g b) -> CoRec f ts -> g b
- Frames: onField :: RPureConstrained c ts => (forall a. (a ∈ ts, c a) => a -> b) -> Field ts -> b
+ Frames: onField :: forall c (ts :: [Type]) b. RPureConstrained c ts => (forall a. (a ∈ ts, c a) => a -> b) -> Field ts -> b
- Frames: rcast :: (RecSubset record rs ss is, RecSubsetFCtx record f) => record f ss -> record f rs
+ Frames: rcast :: forall k1 k2 (rs :: [k1]) (ss :: [k1]) (f :: k2 -> Type) record (is :: [Nat]). (RecSubset record rs ss is, RecSubsetFCtx record f) => record f ss -> record f rs
- Frames: rfield :: Functor f => (a -> f b) -> ElField ((,) s a) -> f (ElField ((,) s b))
+ Frames: rfield :: forall f a b (s :: Symbol). Functor f => (a -> f b) -> ElField '(s, a) -> f (ElField '(s, b))
- Frames: rsubset :: (RecSubset record rs ss is, Functor g, RecSubsetFCtx record f) => (record f rs -> g (record f rs)) -> record f ss -> g (record f ss)
+ Frames: rsubset :: forall k1 k2 (rs :: [k2]) (ss :: [k2]) (f :: k1 -> Type) g record (is :: [Nat]). (RecSubset record rs ss is, Functor g, RecSubsetFCtx record f) => (record f rs -> g (record f rs)) -> record f ss -> g (record f ss)
- Frames: runSafeP :: (MonadMask m, MonadIO m) => Effect (SafeT m) r -> Effect' m r
+ Frames: runSafeP :: forall (m :: Type -> Type) r. (MonadMask m, MonadIO m) => Effect (SafeT m) r -> Effect' m r
- Frames.Categorical: Categorical :: Set Text -> Categorical
+ Frames.Categorical: Categorical :: Set Text -> Categorical (n :: Nat)
- Frames.Categorical: [categories] :: Categorical -> Set Text
+ Frames.Categorical: [categories] :: Categorical (n :: Nat) -> Set Text
- Frames.Melt: melt :: forall vs ts ss proxy. (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, HasLength vs, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> FrameRec ts -> FrameRec (ss ++ '["value" :-> CoRec ElField vs])
+ Frames.Melt: melt :: forall vs ts ss proxy. (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, HasLength vs, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> FrameRec ts -> FrameRec (ss ++ '["value" :-> CoRec ElField vs])
- Frames.Melt: meltAux :: forall vs ss ts. (vs ⊆ ts, ss ⊆ ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => Record ts -> [Record (("value" :-> CoRec ElField vs) : ss)]
+ Frames.Melt: meltAux :: forall vs ss ts. (vs ⊆ ts, ss ⊆ ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => Record ts -> [Record (("value" :-> CoRec ElField vs) : ss)]
- Frames.Melt: meltRow :: (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> Record ts -> [Record (ss ++ '["value" :-> CoRec ElField vs])]
+ Frames.Melt: meltRow :: (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> Record ts -> [Record (ss ++ '["value" :-> CoRec ElField vs])]
- Frames.Melt: meltRow' :: forall proxy vs ts ss. (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> Record ts -> [Record (("value" :-> CoRec ElField vs) : ss)]
+ Frames.Melt: meltRow' :: forall proxy vs ts ss. (vs ⊆ ts, ss ⊆ ts, vs ~ RDeleteAll ss ts, Disjoint ss ts ~ 'True, ts ≅ (vs ++ ss), ColumnHeaders vs, RowToColumn vs vs) => proxy ss -> Record ts -> [Record (("value" :-> CoRec ElField vs) : ss)]
- Frames.RecF: rappend :: () => Rec f as -> Rec f bs -> Rec f (as ++ bs)
+ Frames.RecF: rappend :: forall k (f :: k -> Type) (as :: [k]) (bs :: [k]). Rec f as -> Rec f bs -> Rec f (as ++ bs)
- Frames.RecF: rtraverse :: Applicative h => (forall (x :: u). () => f x -> h (g x)) -> Rec f rs -> h (Rec g rs)
+ Frames.RecF: rtraverse :: forall u h f g (rs :: [u]). Applicative h => (forall (x :: u). () => f x -> h (g x)) -> Rec f rs -> h (Rec g rs)
- Frames.RecF: stripNames' :: (StripFieldNames ts, Functor f) => Rec (f :. ElField) ts -> Rec f (Unlabeled ts)
+ Frames.RecF: stripNames' :: forall (f :: Type -> Type). (StripFieldNames ts, Functor f) => Rec (f :. ElField) ts -> Rec f (Unlabeled ts)
- Frames.RecF: withNames' :: (StripFieldNames ts, Functor f) => Rec f (Unlabeled ts) -> Rec (f :. ElField) ts
+ Frames.RecF: withNames' :: forall (f :: Type -> Type). (StripFieldNames ts, Functor f) => Rec f (Unlabeled ts) -> Rec (f :. ElField) ts
- Frames.TH: RowGen :: [String] -> String -> Separator -> String -> Proxy a -> (Separator -> Producer [Text] (SafeT IO) ()) -> RowGen
+ Frames.TH: RowGen :: [String] -> String -> Separator -> String -> Proxy a -> (Separator -> Producer [Text] (SafeT IO) ()) -> RowGen (a :: [Type])
- Frames.TH: [columnNames] :: RowGen -> [String]
+ Frames.TH: [columnNames] :: RowGen (a :: [Type]) -> [String]
- Frames.TH: [columnUniverse] :: RowGen -> Proxy a
+ Frames.TH: [columnUniverse] :: RowGen (a :: [Type]) -> Proxy a
- Frames.TH: [lineReader] :: RowGen -> Separator -> Producer [Text] (SafeT IO) ()
+ Frames.TH: [lineReader] :: RowGen (a :: [Type]) -> Separator -> Producer [Text] (SafeT IO) ()
- Frames.TH: [rowTypeName] :: RowGen -> String
+ Frames.TH: [rowTypeName] :: RowGen (a :: [Type]) -> String
- Frames.TH: [separator] :: RowGen -> Separator
+ Frames.TH: [separator] :: RowGen (a :: [Type]) -> Separator
- Frames.TH: [tablePrefix] :: RowGen -> String
+ Frames.TH: [tablePrefix] :: RowGen (a :: [Type]) -> String
Files
- CHANGELOG.md +6/−0
- Frames.cabal +4/−4
- src/Frames/CSV.hs +5/−0
- src/Frames/Exploration.hs +39/−2
- src/Frames/ExtraInstances.hs +3/−0
- test/Spec.hs +31/−9
CHANGELOG.md view
@@ -1,3 +1,9 @@+# 0.7.1++- Add `showFrame`, `printFrame`, `takeRows`, and `dropRows` to the `Frames.Exploration` module. These helpers for working with `Frames` are re-exported from the `Frames` module itself. Thanks to @chfin.++- GHC-9.0.1 support.+ # 0.7.0 GHC-8.10 support in Vinyl requires a major version bump.
Frames.cabal view
@@ -1,5 +1,5 @@ name: Frames-version: 0.7.0+version: 0.7.1 synopsis: Data frames For working with tabular data files description: User-friendly, type safe, runtime efficient tooling for working with tabular data deserialized from@@ -30,7 +30,7 @@ data/left1.csv data/right1.csv data/left_summary.csv data/FL2.csv cabal-version: >=1.10-tested-with: GHC == 8.4.4 || == 8.6.5 || == 8.8.3 || == 8.10.1+tested-with: GHC == 8.4.4 || == 8.6.5 || == 8.8.4 || == 8.10.4 || == 9.0.1 source-repository head type: git@@ -64,8 +64,8 @@ TypeOperators, ConstraintKinds, StandaloneDeriving, UndecidableInstances, ScopedTypeVariables, OverloadedStrings, TypeApplications- build-depends: base >=4.10 && <4.15,- ghc-prim >=0.3 && <0.7,+ build-depends: base >=4.10 && <4.16,+ ghc-prim >=0.3 && <0.8, primitive >= 0.6 && < 0.8, text >= 1.1.1.0, template-haskell,
src/Frames/CSV.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE CPP, DataKinds, DeriveLift, FlexibleContexts, FlexibleInstances, GADTs, LambdaCase, OverloadedStrings, RankNTypes, ScopedTypeVariables, TemplateHaskell, TypeApplications,@@ -68,7 +69,11 @@ hs' = [|map T.pack $(listE $ map (stringE . T.unpack) hs)|] quoting' = lift quoting #if MIN_VERSION_template_haskell(2,16,0)+#if MIN_VERSION_template_haskell(2,17,0)+ liftTyped = liftCode . unsafeTExpCoerce . lift+#else liftTyped = unsafeTExpCoerce . lift+#endif #endif -- | Default 'ParseOptions' get column names from a header line, and
src/Frames/Exploration.hs view
@@ -10,8 +10,11 @@ -- | Functions useful for interactively exploring and experimenting -- with a data set. module Frames.Exploration (pipePreview, select, lenses, recToList,- pr, pr1) where+ pr, pr1, showFrame, printFrame,+ takeRows, dropRows) where import Data.Char (isSpace, isUpper)+import qualified Data.Foldable as F+import Data.List (intercalate) import Data.Proxy import qualified Data.Vinyl as V import qualified Data.Vinyl.Class.Method as V@@ -21,8 +24,11 @@ import Language.Haskell.TH import Language.Haskell.TH.Quote import Pipes hiding (Proxy)+import qualified Pipes as P import qualified Pipes.Prelude as P import Pipes.Safe (SafeT, runSafeT, MonadMask)+import Frames.Frame (Frame(Frame))+import Frames.RecF (columnHeaders, ColumnHeaders) -- * Preview Results @@ -90,7 +96,7 @@ (V.RecMapMethod ((~) a) ElField rs, V.RecordToList rs) => Record rs -> [a] recToList = V.recordToList . V.rmapMethod @((~) a) aux- where aux :: a ~ (V.PayloadType ElField t) => V.ElField t -> Const a t+ where aux :: a ~ V.PayloadType ElField t => V.ElField t -> Const a t aux (Field x) = Const x -- * Helpers@@ -107,3 +113,34 @@ -- | Remove white space from both ends of a 'String'. strip :: String -> String strip = takeWhile (not . isSpace) . dropWhile isSpace++-- | @takeRows n frame@ produces a new 'Frame' made up of the first+-- @n@ rows of @frame@.+takeRows :: Int -> Frame (Record rs) -> Frame (Record rs)+takeRows n (Frame len rows) = Frame (min n len) rows++-- | @dropRows n frame@ produces a new 'Frame' just like @frame@, but+-- not including its first @n@ rows.+dropRows :: Int -> Frame (Record rs) -> Frame (Record rs)+dropRows n (Frame len rows) = Frame (max 0 (len - n)) (\i -> rows (i + n))++-- | Format a 'Frame' to a 'String'.+showFrame :: forall rs.+ (ColumnHeaders rs, V.RecMapMethod Show ElField rs, V.RecordToList rs)+ => String -- ^ Separator between fields+ -> Frame (Record rs) -- ^ The 'Frame' to be formatted to a 'String'+ -> String+showFrame sep frame =+ unlines (intercalate sep (columnHeaders (Proxy :: Proxy (Record rs))) : rows)+ where rows = P.toList (F.mapM_ (P.yield . intercalate sep . showFields) frame)++-- | Print a 'Frame' to 'System.IO.stdout'.+printFrame :: forall rs.+ (ColumnHeaders rs, V.RecMapMethod Show ElField rs, V.RecordToList rs)+ => String -- ^ Separator between fields+ -> Frame (Record rs) -- ^ The 'Frame' to be printed to @stdout@+ -> IO ()+printFrame sep frame = do+ putStrLn (intercalate sep (columnHeaders (Proxy :: Proxy (Record rs))))+ P.runEffect (rows >-> P.stdoutLn)+ where rows = F.mapM_ (P.yield . intercalate sep . showFields) frame
src/Frames/ExtraInstances.hs view
@@ -49,8 +49,11 @@ instance NFData1 VF.Identity where liftRnf r = r . getIdentity +#if MIN_VERSION_vinyl(0,13,1)+#else instance (NFData (f r), NFData (Rec f rs)) => NFData (Rec f (r ': rs)) where rnf (x :& xs) = rnf x `seq` rnf xs+#endif instance NFData (Rec f '[]) where rnf RNil = ()
test/Spec.hs view
@@ -10,7 +10,7 @@ import Data.List (find, isPrefixOf) import Data.Monoid (First(..)) import qualified Data.Text as T-import Language.Haskell.TH as TH+import qualified Language.Haskell.TH as TH import Language.Haskell.TH.Syntax (addDependentFile) import Frames import Frames.CSV (produceCSV)@@ -25,7 +25,7 @@ import Test.HUnit.Lang (assertFailure) import qualified LatinTest as Latin-import qualified Issue114 as Issue114+import qualified Issue114 import qualified Issue145 import qualified NoHeader import qualified Categorical@@ -33,15 +33,16 @@ import qualified UncurryFold import qualified UncurryFoldNoHeader import qualified UncurryFoldPartialData+import Data.Vinyl (xrec) -- | 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)+ TH.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@@ -67,7 +68,7 @@ -- -- 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 :: String -> TH.Q TH.Exp manualGeneration k = do csvExamples <- TH.runIO (examplesFrom "test/examples.toml") maybe (error ("Table " ++ k ++ " not found")) (generateCode "Row")@@ -106,10 +107,10 @@ main = do hspec $ do-#if __GLASGOW_HASKELL__ >= 804+ describe "Haskell type generation" $ mapM_ (\(CsvExample k _ g, g') -> it k (Code g' `shouldBe` Code g)) csvTests-#endif+ -- describe "Multiple tables" $ -- do _g <- H.runIO $ -- generatedFrom "test/examples.toml" "managers_employees"@@ -144,7 +145,7 @@ unless (frame == frame2) (assertFailure "Reparsed CSV differs from input") describe "Latin1 Text Encoding" $- do managers <- H.runIO (Latin.managers)+ do managers <- H.runIO Latin.managers it "Parses" $ managers `shouldBe` ["João", "Esperança"] describe "Skip Missing Data" $ do@@ -225,3 +226,24 @@ streamedChunks <- H.runIO Chunks.chunkStream it "Can split an input stream into Frame chunks" $ streamedChunks `shouldBe` everyTenthEducation+ describe "Printing with take and drop" $ do+ let frame :: Frame (Record '[ "id" :-> Int, "manager" :-> Text+ , "age" :-> Int, "pay" :-> Double ])+ frame = toFrame [ xrec (1, "Joe", 53, 80000)+ , xrec (2, "Sarah", 44, 80000) ]+ it "Can format a Frame to a String" $+ showFrame "\t" frame `shouldBe`+ unlines [+ "id\tmanager\tage\tpay"+ , "1\t\"Joe\"\t53\t80000.0"+ , "2\t\"Sarah\"\t44\t80000.0" ]+ it "Can take the first row" $+ showFrame "\t" (takeRows 1 frame) `shouldBe`+ unlines [+ "id\tmanager\tage\tpay"+ , "1\t\"Joe\"\t53\t80000.0" ]+ it "Can drop the first row" $+ showFrame "\t" (dropRows 1 frame) `shouldBe`+ unlines [+ "id\tmanager\tage\tpay"+ , "2\t\"Sarah\"\t44\t80000.0" ]