Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Frames.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ executable kata04
hs-source-dirs: demo
default-language: Haskell2010

executable modcsv
if !flag(demos)
buildable: False
main-is: ModifyCSV.hs
if flag(demos)
build-depends: base, Frames, microlens, pipes
hs-source-dirs: demo
default-language: Haskell2010

test-suite spec
type: exitcode-stdio-1.0
hs-source-dirs: test
Expand Down
23 changes: 23 additions & 0 deletions demo/ModifyCSV.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{-# language DataKinds, FlexibleContexts, TemplateHaskell #-}
-- | A demonstration of ingesting a CSV file, modifying the data (in
-- this case multiplying a column by 2), then writing it back out to a
-- new CSV file.
import Frames
import Frames.CSV (pipeToCSV, consumeTextLines)
import Lens.Micro
import Pipes ((>->), Effect)
import qualified Pipes.Prelude as P

tableTypes "Row" "data/data1.csv"

myFun :: Row -> Row
myFun = age %~ (*2)

myPipeline :: MonadSafe m => Effect m ()
myPipeline = readTable "data/data1.csv"
>-> P.map myFun
>-> pipeToCSV
>-> consumeTextLines "data/dataMod.csv"

main :: IO ()
main = runSafeEffect myPipeline
28 changes: 25 additions & 3 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{ compiler ? "ghc863"
{ compiler ? "ghc865"
, withHoogle ? true
}:

let
pkgs = import <nixpkgs> {};
all-hies = import (fetchTarball "https://github.com/infinisil/all-hies/tarball/master") {};
hie = all-hies.selection { selector = p: { inherit (p) ghc865; };};
f = import ./default.nix;
packageSet = pkgs.haskell.packages.${compiler};
hspkgs = (
if withHoogle then
packageSet.override {
overrides = (self: super: {
ghc = super.ghc // { withPackages = f: super.ghc.withHoogle (ps: f ps ++ [ ps.cabal-install ]); };
ghc = super.ghc // { withPackages = f: super.ghc.withHoogle (ps: f ps ++ [ ]); };
# Cabal = super.Cabal_2_4_1_0;
vinyl = pkgs.haskell.lib.dontBenchmark (super.callPackage ~/Projects/Vinyl {});
# pipes-safe = super.callCabal2nix "pipes-safe" (pkgs.fetchFromGitHub {
# owner = "Gabriel439";
Expand All @@ -19,6 +22,12 @@ packageSet = pkgs.haskell.packages.${compiler};
# sha256 = "0i2l36xwqskhm5kcdvmyfy2n7cf2i2qdsrap5nib825gq2mnv9z5";
# }) {};
# Chart = super.callHackage "Chart" "1.9" {};
cabal-install-2_4 = super.callHackage "cabal-install" "2.4.1.0" {
Cabal = super.Cabal_2_4_1_0;
};
hackage-security = pkgs.haskell.lib.dontCheck (super.callHackage "hackage-security" "0.5.3.0" {
Cabal = super.Cabal_2_4_1_0;
});
SVGFonts = pkgs.haskell.lib.doJailbreak (super.callHackage "SVGFonts" "1.6.0.3" {});
# intero = pkgs.haskell.lib.dontCheck (super.callPackage ~/src/intero {});
Chart = super.callCabal2nix "Chart" (pkgs.fetchFromGitHub {
Expand All @@ -40,5 +49,18 @@ packageSet = pkgs.haskell.packages.${compiler};
else packageSet
);
drv = hspkgs.callPackage f {};
ghc = hspkgs.ghc.withHoogle (_: drv.passthru.getBuildInputs.haskellBuildInputs);
in
if pkgs.lib.inNixShell then drv.env else drv
# if pkgs.lib.inNixShell then drv.env else drv
pkgs.mkShell {
buildInputs = [ hie # hspkgs.cabal-install
hspkgs.cabal-install-2_4
ghc ];
shellHook = ''
export NIX_GHC='${ghc}/bin/ghc'
export NIX_GHCPKG='${ghc}/bin/ghc-pkg'
export NIX_GHC_DOCDIR='${drv.compiler.doc}/share/doc/ghc/html'
export NIX_GHC_LIBDIR='${ghc}/lib/${drv.compiler.name}'
export HIE_HOOGLE_DATABASE='${ghc}/share/doc/hoogle/default.hoo'
'';
}
4 changes: 2 additions & 2 deletions src/Frames/CSV.hs
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,12 @@ produceCSV recs = do
-- streaming input that you wish to use to produce streaming output.
pipeToCSV :: forall ts m.
(Monad m, ColumnHeaders ts, RecordToList ts,
RecMapMethod Show ElField ts)
RecMapMethod ShowCSV ElField ts)
=> P.Pipe (Record ts) T.Text m ()
pipeToCSV = P.yield (T.intercalate "," (map T.pack header)) >> go
where header = columnHeaders (Proxy :: Proxy (Record ts))
go :: P.Pipe (Record ts) T.Text m ()
go = P.map (T.intercalate "," . map T.pack . showFields)
go = P.map (T.intercalate "," . showFieldsCSV)

-- | Write a header row with column names followed by a line of text
-- for each 'Record' to the given file.
Expand Down
2 changes: 1 addition & 1 deletion src/Frames/Frame.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ boxedFrame xs = Frame (V.length v) (v V.!)

instance Eq r => Eq (Frame r) where
Frame l1 r1 == Frame l2 r2 =
l1 == l2 && and (map (\i -> r1 i == r2 i) [0 .. l1 - 1])
l1 == l2 && all (\i -> r1 i == r2 i) [0 .. l1 - 1]

-- | The 'Monoid' instance for 'Frame' provides a mechanism for
-- vertical concatenation of 'Frame's. That is, @f1 <> f2@ will return
Expand Down
1 change: 1 addition & 0 deletions src/Frames/ShowCSV.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ instance ShowCSV Bool where
instance ShowCSV Int where
instance ShowCSV Double where
instance ShowCSV Text where
showCSV = id
9 changes: 5 additions & 4 deletions test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,11 @@ main = do
-- The test data isn't formatted quite how we'd do it: text
-- fields aren't quoted, and salaries represented as Doubles
-- do not have decimal points.
let csvInput' = T.replace "Joe" "\"Joe\""
. T.replace "Sarah" "\"Sarah\""
. T.replace "\"80,000\"" "80000.0"
$ T.pack csvInput
-- let csvInput' = T.replace "Joe" "\"Joe\""
-- . T.replace "Sarah" "\"Sarah\""
-- . T.replace "\"80,000\"" "80000.0"
-- $ T.pack csvInput
let csvInput' = T.replace "\"80,000\"" "80000.0" (T.pack csvInput)
it "Produces expected output" $
T.unlines (map T.pack csvOutput) `shouldBe` csvInput'
it "Produces parseable output" $
Expand Down