{-# LANGUAGE DataKinds, FlexibleContexts, MultiParamTypeClasses, OverloadedLabels, OverloadedStrings, TemplateHaskell, TypeApplications, TypeFamilies, TypeOperators #-} module Categorical where import Data.Vinyl.Derived import Data.Vinyl.XRec ( toHKD ) import Frames import Frames.Categorical ( declareCategorical ) import Frames.TH ( rowGenCat , RowGen(..) , declarePrefixedColumn ) import Pipes ( Producer , (>->) ) import qualified Pipes.Prelude as P -- * Automatically inferred categorical data types tableTypes' (rowGenCat "test/data/catSmall.csv") { rowTypeName = "Small", tablePrefix = "S" } tableTypes' (rowGenCat "test/data/catLarge.csv") { rowTypeName = "Large", tablePrefix = "L" } -- Frames will, by default, generate a categorical type if there are -- no more than 8 variants. In our small data file, five distinct -- months appear, so a data type is generated and used. fifthMonthSmall :: IO (Maybe SmallMonth) fifthMonthSmall = fmap (fmap (rvalf #month)) . runSafeEffect . P.head $ load >-> P.drop 4 where load :: MonadSafe m => Producer Small m () load = readTableOpt smallParser "test/data/catSmall.csv" -- When every month appears, Frames leaves that column's type as 'Text'. fifthMonthLarge :: IO (Maybe Text) fifthMonthLarge = fmap (fmap (rvalf #month)) . runSafeEffect . P.head $ load >-> P.drop 4 where load :: MonadSafe m => Producer Large m () load = readTableOpt largeParser "test/data/catLarge.csv" -- * Custom categorical type -- We may want to use a sum type for which not every variant appears -- in a data file. We can specify the variants ourselves. declareCategorical "MyMonthData" (Just "My") ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"] declarePrefixedColumn "month" "my" ''MyMonthData -- We could re-use the @SId@ or @LId@ types generated above, but we -- will declare our own @MyId@ type here for the sake of completeness. declarePrefixedColumn "id" "my" ''Int -- This is the row type we will parse into type MyRow = Record '[MyId, MyMonth] -- Here we attempt to parse the @catSmall.csv@ data file into our -- custom @MyMonthData@ type that uses three-letter abbreviations, but -- allow our parses to fail. It turns out that @"May"@ still -- parses. We use 'toHKD' to cut through the noise of the @(Maybe -- :. ElField)@ interpretation we parsed into. fifthMonthCustom :: IO (Maybe MyMonthData) fifthMonthCustom = fmap (>>= toHKD . rgetf #month) . runSafeEffect . P.head $ load >-> P.drop 4 where load :: MonadSafe m => Producer (ColFun Maybe MyRow) m () load = readTableMaybe "test/data/catSmall.csv"