Frames-map-reduce 0.1.0.1 → 0.2.0.0
raw patch · 9 files changed
+1000/−20 lines, 9 filesdep ~profunctorsPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: profunctors
API changes (from Hackage documentation)
+ Frames.Folds: FoldFieldEndo :: EndoFold (f a) -> FoldFieldEndo f a
+ Frames.Folds: [unFoldFieldEndo] :: FoldFieldEndo f a -> EndoFold (f a)
+ Frames.Folds: class (Newtype (f a) a, Monoid (f a)) => MonoidalField f a
+ Frames.Folds: fieldToFieldFold :: forall x y rs. (KnownField x, KnownField y, ElemOf rs x) => Fold (Snd x) (Snd y) -> FoldRecord ElField rs y
+ Frames.Folds: monoidWrapperToFold :: forall f a. (Newtype (f a) a, Monoid (f a)) => Fold a a
+ Frames.Folds: newtype FoldFieldEndo f a
+ Frames.Folds.General: FoldEndo :: EndoFold (f (Snd t)) -> FoldEndo f t
+ Frames.Folds.General: FoldRecord :: Fold (record (f :. ElField) rs) (g a) -> FoldRecord record f g rs a
+ Frames.Folds.General: [unFoldEndo] :: FoldEndo f t -> EndoFold (f (Snd t))
+ Frames.Folds.General: [unFoldRecord] :: FoldRecord record f g rs a -> Fold (record (f :. ElField) rs) (g a)
+ Frames.Folds.General: class (c (Snd t)) => ConstrainedField c t
+ Frames.Folds.General: class EndoFieldFoldsToRecordFolds rs record f
+ Frames.Folds.General: fieldToFieldFold :: forall x y rs record f. (KnownField x, KnownField y, ElemOf rs x, RecGetFieldC x record f rs, Applicative f) => (forall z. f z -> Maybe z) -> Fold (Snd x) (Snd y) -> FoldRecord record f (f :. ElField) rs y
+ Frames.Folds.General: foldAll :: (RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record f, IsoRec rs record f, Functor f) => (forall a. Fold a a) -> Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.Folds.General: foldAllConstrained :: forall c rs record f. (RPureConstrained (ConstrainedField c) rs, RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record f, IsoRec rs record f, Applicative f) => (forall a. f a -> Maybe a) -> (forall a. c a => Fold a a) -> Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.Folds.General: foldAllMonoid :: forall g rs record f. (RPureConstrained (ConstrainedField (MonoidalField g)) rs, RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record f, IsoRec rs record f, Applicative f) => (forall a. f a -> Maybe a) -> Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.Folds.General: functorFoldAllConstrained :: forall c rs record f. (RPureConstrained (ConstrainedField c) rs, RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record f, IsoRec rs record f, Applicative f) => (forall a. c a => Fold (f a) (f a)) -> Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.Folds.General: instance (Frames.Folds.General.EndoFieldFoldsToRecordFolds rs record f, Frames.MapReduce.General.RCastC rs (r : rs) record f, Data.Vinyl.Derived.KnownField r, Frames.MapReduce.General.RecGetFieldC r record f (r : rs), Data.Vinyl.Core.RMap rs) => Frames.Folds.General.EndoFieldFoldsToRecordFolds (r : rs) record f
+ Frames.Folds.General: instance Frames.Folds.General.EndoFieldFoldsToRecordFolds '[] record f
+ Frames.Folds.General: instance forall k1 k (c :: k -> GHC.Types.Constraint) (t :: (k1, k)). c (Data.Vinyl.TypeLevel.Snd t) => Frames.Folds.General.ConstrainedField c t
+ Frames.Folds.General: newtype FoldEndo f t
+ Frames.Folds.General: newtype FoldRecord record f g rs a
+ Frames.Folds.General: recFieldF :: forall t rs a record f. (KnownField t, Applicative f) => (forall x. f x -> Maybe x) -> Fold a (Snd t) -> (record (f :. ElField) rs -> f a) -> FoldRecord record f (f :. ElField) rs t
+ Frames.Folds.General: sequenceEndoFolds :: forall rs record f. (RApply rs, RPureConstrained KnownField rs, EndoFieldFoldsToRecordFolds rs record f, IsoRec rs record f, Functor f) => Rec (FoldEndo f) rs -> Fold (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.Folds.General: sequenceRecFold :: forall as rs record f. IsoRec rs record f => Rec (FoldRecord record f (f :. ElField) as) rs -> Fold (record (f :. ElField) as) (record (f :. ElField) rs)
+ Frames.Folds.General: type EndoFold a = Fold a a
+ Frames.Folds.Maybe: FoldEndo :: EndoFold (f (Snd t)) -> FoldEndo f t
+ Frames.Folds.Maybe: FoldRecord :: Fold (record (f :. ElField) rs) (g a) -> FoldRecord record f g rs a
+ Frames.Folds.Maybe: [unFoldEndo] :: FoldEndo f t -> EndoFold (f (Snd t))
+ Frames.Folds.Maybe: [unFoldRecord] :: FoldRecord record f g rs a -> Fold (record (f :. ElField) rs) (g a)
+ Frames.Folds.Maybe: fieldToFieldFold :: forall x y rs record. (KnownField x, KnownField y, ElemOf rs x, RecGetFieldC x record Maybe rs) => Fold (Snd x) (Snd y) -> FoldRecord record Maybe (Maybe :. ElField) rs y
+ Frames.Folds.Maybe: foldAll :: (RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record Maybe, IsoRec rs record Maybe) => (forall a. Fold a a) -> Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.Folds.Maybe: foldAllConstrained :: forall c rs record. (RPureConstrained (ConstrainedField c) rs, RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record Maybe, IsoRec rs record Maybe) => (forall a. c a => Fold a a) -> Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.Folds.Maybe: foldAllMonoid :: forall g rs record. (RPureConstrained (ConstrainedField (MonoidalField g)) rs, RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record Maybe, IsoRec rs record Maybe) => Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.Folds.Maybe: maybeFoldAllConstrained :: forall c rs record. (RPureConstrained (ConstrainedField c) rs, RPureConstrained KnownField rs, RApply rs, EndoFieldFoldsToRecordFolds rs record Maybe, IsoRec rs record Maybe) => (forall a. c a => Fold (Maybe a) (Maybe a)) -> Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.Folds.Maybe: newtype FoldEndo f t
+ Frames.Folds.Maybe: newtype FoldRecord record f g rs a
+ Frames.Folds.Maybe: recFieldF :: forall t rs a record. KnownField t => Fold a (Snd t) -> (record (Maybe :. ElField) rs -> Maybe a) -> FoldRecord record Maybe (Maybe :. ElField) rs t
+ Frames.Folds.Maybe: sequenceEndoFolds :: forall rs record. (RApply rs, RPureConstrained KnownField rs, EndoFieldFoldsToRecordFolds rs record Maybe, IsoRec rs record Maybe) => Rec (FoldEndo Maybe) rs -> Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.Folds.Maybe: sequenceRecFold :: forall as rs record. IsoRec rs record Maybe => Rec (FoldRecord record Maybe (Maybe :. ElField) as) rs -> Fold (record (Maybe :. ElField) as) (record (Maybe :. ElField) rs)
+ Frames.Folds.Maybe: type EndoFold a = Fold a a
+ Frames.MapReduce.General: assignKeys :: forall ks rs record f. RCastC ks rs record f => Assign (record (f :. ElField) ks) (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.MapReduce.General: assignKeysAndData :: forall ks cs rs record f. (RCastC ks rs record f, RCastC cs rs record f) => Assign (record (f :. ElField) ks) (record (f :. ElField) rs) (record (f :. ElField) cs)
+ Frames.MapReduce.General: class IsoRec rs record f
+ Frames.MapReduce.General: class RCastC rs ss record f
+ Frames.MapReduce.General: class RecGetFieldC t record f rs
+ Frames.MapReduce.General: foldAndAddKey :: (IsoRec ks record f, IsoRec cs record f, IsoRec (ks ++ cs) record f) => Fold x (record (f :. ElField) cs) -> Reduce (record (f :. ElField) ks) x (record (f :. ElField) (ks ++ cs))
+ Frames.MapReduce.General: fromRec :: IsoRec rs record f => Rec (f :. ElField) rs -> record (f :. ElField) rs
+ Frames.MapReduce.General: instance (Data.Vinyl.Core.RPureConstrained (Data.Vinyl.SRec.FieldOffset (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) ss) rs, Data.Vinyl.Core.RPureConstrained (Data.Vinyl.SRec.FieldOffset (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) rs) rs, Data.Vinyl.Core.RFoldMap rs, Data.Vinyl.Core.RMap rs, Data.Vinyl.Core.RApply rs, Foreign.Storable.Storable (Data.Vinyl.Core.Rec (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) rs)) => Frames.MapReduce.General.RCastC rs ss Data.Vinyl.SRec.SRec f
+ Frames.MapReduce.General: instance (Data.Vinyl.Derived.KnownField t, GHC.Base.Functor f, Frames.MapReduce.General.RecGetFieldC t record f (t : rs), Frames.MapReduce.General.RCastC rs (t : rs) record f, Data.Hashable.Class.Hashable (f (Data.Vinyl.TypeLevel.Snd t)), Data.Hashable.Class.Hashable (record (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) rs)) => Data.Hashable.Class.Hashable (record (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) (t : rs))
+ Frames.MapReduce.General: instance (Data.Vinyl.TypeLevel.IndexWitnesses (Data.Vinyl.TypeLevel.RImage rs ss), Data.Vinyl.TypeLevel.NatToInt (Data.Vinyl.TypeLevel.RLength rs)) => Frames.MapReduce.General.RCastC rs ss Data.Vinyl.ARec.ARec f
+ Frames.MapReduce.General: instance (Data.Vinyl.TypeLevel.NatToInt (Data.Vinyl.TypeLevel.RLength rs), Data.Vinyl.Core.RecApplicative rs, Data.Vinyl.Core.RPureConstrained (Data.Vinyl.ARec.IndexableField rs) rs) => Frames.MapReduce.General.IsoRec rs Data.Vinyl.ARec.ARec f
+ Frames.MapReduce.General: instance Data.Vinyl.Lens.RecSubset Data.Vinyl.Core.Rec rs ss (Data.Vinyl.TypeLevel.RImage rs ss) => Frames.MapReduce.General.RCastC rs ss Data.Vinyl.Core.Rec f
+ Frames.MapReduce.General: instance Data.Vinyl.SRec.FieldOffset (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) rs t => Frames.MapReduce.General.RecGetFieldC t Data.Vinyl.SRec.SRec f rs
+ Frames.MapReduce.General: instance Foreign.Storable.Storable (Data.Vinyl.Core.Rec (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) rs) => Frames.MapReduce.General.IsoRec rs Data.Vinyl.SRec.SRec f
+ Frames.MapReduce.General: instance Frames.MapReduce.General.IsoRec rs Data.Vinyl.Core.Rec f
+ Frames.MapReduce.General: instance Frames.MapReduce.General.RecGetFieldC t Data.Vinyl.ARec.ARec f rs
+ Frames.MapReduce.General: instance Frames.MapReduce.General.RecGetFieldC t Data.Vinyl.Core.Rec f rs
+ Frames.MapReduce.General: instance forall k (record :: ((GHC.Types.Symbol, *) -> *) -> [k] -> *) (f :: * -> *). Data.Hashable.Class.Hashable (record (f Data.Vinyl.Functor.:. Data.Vinyl.Functor.ElField) '[])
+ Frames.MapReduce.General: makeRecsWithKey :: (Functor g, Foldable g, IsoRec ks record f, IsoRec as record f, IsoRec (ks ++ as) record f) => (y -> record (f :. ElField) as) -> Reduce (record (f :. ElField) ks) x (g y) -> Reduce (record (f :. ElField) ks) x (g (record (f :. ElField) (ks ++ as)))
+ Frames.MapReduce.General: makeRecsWithKeyM :: (Monad m, Functor g, Foldable g, IsoRec ks record f, IsoRec as record f, IsoRec (ks ++ as) record f) => (y -> record (f :. ElField) as) -> ReduceM m (record (f :. ElField) ks) x (g y) -> ReduceM m (record (f :. ElField) ks) x (g (record (f :. ElField) (ks ++ as)))
+ Frames.MapReduce.General: rcastF :: RCastC rs ss record f => record (f :. ElField) ss -> record (f :. ElField) rs
+ Frames.MapReduce.General: reduceAndAddKey :: forall ks cs x record f. (IsoRec ks record f, IsoRec cs record f, IsoRec (ks ++ cs) record f) => (forall h. Foldable h => h x -> record (f :. ElField) cs) -> Reduce (record (f :. ElField) ks) x (record (f :. ElField) (ks ++ cs))
+ Frames.MapReduce.General: rgetF :: (RecGetFieldC t record f rs, KnownField t, ElemOf rs t) => record (f :. ElField) rs -> (f :. ElField) t
+ Frames.MapReduce.General: rgetFieldF :: (RecGetFieldC t record f rs, KnownField t, Functor f, ElemOf rs t) => record (f :. ElField) rs -> f (Snd t)
+ Frames.MapReduce.General: splitOnKeys :: forall ks rs cs record f. (RCastC ks rs record f, RCastC cs rs record f, cs ~ RDeleteAll ks rs) => Assign (record (f :. ElField) ks) (record (f :. ElField) rs) (record (f :. ElField) cs)
+ Frames.MapReduce.General: toRec :: IsoRec rs record f => record (f :. ElField) rs -> Rec (f :. ElField) rs
+ Frames.MapReduce.General: unpackFilterOnField :: forall t rs record f. (Functor f, KnownField t, ElemOf rs t, RecGetFieldC t record f rs) => (f (Snd t) -> Bool) -> Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.MapReduce.General: unpackFilterOnGoodField :: forall t rs record f. (Functor f, KnownField t, ElemOf rs t, RecGetFieldC t record f rs) => (forall a. f a -> Maybe a) -> (Snd t -> Bool) -> Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.MapReduce.General: unpackFilterRow :: (record (f :. ElField) rs -> Bool) -> Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.MapReduce.General: unpackGoodRows :: forall cs rs record f. RCastC cs rs record f => (record (f :. ElField) cs -> Bool) -> Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.MapReduce.General: unpackNoOp :: Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)
+ Frames.MapReduce.Maybe: assignKeys :: forall ks rs record. RCastC ks rs record Maybe => Assign (record (Maybe :. ElField) ks) (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.MapReduce.Maybe: assignKeysAndData :: forall ks cs rs record. (RCastC ks rs record Maybe, RCastC cs rs record Maybe) => Assign (record (Maybe :. ElField) ks) (record (Maybe :. ElField) rs) (record (Maybe :. ElField) cs)
+ Frames.MapReduce.Maybe: foldAndAddKey :: (IsoRec ks record Maybe, IsoRec cs record Maybe, IsoRec (ks ++ cs) record Maybe, RecVec (ks ++ cs)) => Fold x (record (Maybe :. ElField) cs) -> Reduce (record (Maybe :. ElField) ks) x (record (Maybe :. ElField) (ks ++ cs))
+ Frames.MapReduce.Maybe: makeRecsWithKey :: (Functor g, Foldable g, IsoRec ks record Maybe, IsoRec as record Maybe, IsoRec (ks ++ as) record Maybe, RecVec (ks ++ as)) => (y -> record (Maybe :. ElField) as) -> Reduce (record (Maybe :. ElField) ks) x (g y) -> Reduce (record (Maybe :. ElField) ks) x (g (record (Maybe :. ElField) (ks ++ as)))
+ Frames.MapReduce.Maybe: makeRecsWithKeyM :: (Monad m, Functor g, Foldable g, IsoRec ks record Maybe, IsoRec as record Maybe, IsoRec (ks ++ as) record Maybe, RecVec (ks ++ as)) => (y -> record (Maybe :. ElField) as) -> ReduceM m (record (Maybe :. ElField) ks) x (g y) -> ReduceM m (record (Maybe :. ElField) ks) x (g (record (Maybe :. ElField) (ks ++ as)))
+ Frames.MapReduce.Maybe: reduceAndAddKey :: forall ks cs x record. (IsoRec ks record Maybe, IsoRec cs record Maybe, IsoRec (ks ++ cs) record Maybe, RecVec (ks ++ cs)) => (forall h. Foldable h => h x -> record (Maybe :. ElField) cs) -> Reduce (record (Maybe :. ElField) ks) x (record (Maybe :. ElField) (ks ++ cs))
+ Frames.MapReduce.Maybe: splitOnKeys :: forall ks rs cs record. (RCastC ks rs record Maybe, RCastC cs rs record Maybe, cs ~ RDeleteAll ks rs) => Assign (record (Maybe :. ElField) ks) (record (Maybe :. ElField) rs) (record (Maybe :. ElField) cs)
+ Frames.MapReduce.Maybe: unpackFilterOnField :: forall t rs record. (KnownField t, ElemOf rs t, RecGetFieldC t record Maybe rs) => (Maybe (Snd t) -> Bool) -> Unpack (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.MapReduce.Maybe: unpackFilterRow :: (record (Maybe :. ElField) rs -> Bool) -> Unpack (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.MapReduce.Maybe: unpackGoodRecRows :: forall cs rs record. RCastC cs rs Rec Maybe => Unpack (Rec (Maybe :. ElField) rs) (Rec (Maybe :. ElField) rs)
+ Frames.MapReduce.Maybe: unpackGoodRows :: forall cs rs record. RCastC cs rs record Maybe => (record (Maybe :. ElField) cs -> Bool) -> Unpack (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
+ Frames.MapReduce.Maybe: unpackNoOp :: Unpack (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)
Files
- CHANGELOG.md +9/−0
- Frames-map-reduce.cabal +8/−3
- examples/AddRowsByLabel.hs +69/−14
- src/Frames/Folds.hs +7/−1
- src/Frames/Folds/General.hs +277/−0
- src/Frames/Folds/Maybe.hs +177/−0
- src/Frames/MapReduce.hs +4/−2
- src/Frames/MapReduce/General.hs +258/−0
- src/Frames/MapReduce/Maybe.hs +191/−0
CHANGELOG.md view
@@ -1,3 +1,12 @@+v 0.2.0.0 +* Added Combinators for ```record (Maybe :. ElField) rs``` (Much thanks to Tim Pierson, @o1lo01ol1o, for the idea and the work!).+* Added Combinators polymorphic in record type (```Rec```, ```ARec``` or ```SRec``` are supported) and composed interpretation functor ```f :. ElField```+* ```Maybe :. ElField``` case now uses the general case in order to avoid code duplication+* ```Record``` case still uses separate code because the amount of new coercion code required was enough to not seem worth the re-use of the polymorphic case.++v 0.1.0.2 +* Changed example from "executable" to "test-suite". Now it doens't need to be built and its dependencies don't leak to library users.+ v 0.1.0.1 * lowered containers lower bound * rasied some outdated upper bounds (profunctors, hashable)
Frames-map-reduce.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2 name: Frames-map-reduce-version: 0.1.0.1+version: 0.2.0.0 synopsis: Frames wrapper for map-reduce-folds and some extra folds helpers. description: Frames-map-reduce provides some helpers for using the map-reduce-folds library with vinyl records and Frames. These include functions for filtering Frames, splitting records into key columns and data columns and@@ -26,21 +26,26 @@ library ghc-options: -Wall -funbox-strict-fields exposed-modules: Frames.Folds+ , Frames.Folds.Maybe+ , Frames.Folds.General , Frames.MapReduce+ , Frames.MapReduce.Maybe+ , Frames.MapReduce.General build-depends: Frames >= 0.6.1 && < 0.7, base >= 4.12.0 && < 4.13, containers >= 0.5.0 && < 0.7, hashable >= 1.2.7 && < 1.4, map-reduce-folds >= 0.1.0.0, - profunctors >= 5.3 && < 5.5,+ profunctors >= 5.3 && < 5.6, vinyl >= 0.11.0 && < 0.12, foldl >= 1.4.5 && < 1.5, newtype >= 0.2 && < 0.3 hs-source-dirs: src default-language: Haskell2010 -executable AddRowsByLabel+test-suite AddRowsByLabel+ type: exitcode-stdio-1.0 main-is: AddRowsByLabel.hs hs-source-dirs: examples ghc-options: -Wall
examples/AddRowsByLabel.hs view
@@ -1,26 +1,33 @@-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE InstanceSigs #-} module Main where--import qualified Frames.MapReduce as FMR-import qualified Frames.Folds as FF--import qualified Frames as F-import qualified Data.Vinyl as V+import qualified Control.Foldl as FL import qualified Data.List as L import qualified Data.Text as T-import qualified Control.Foldl as FL-import Data.Monoid ( Sum )+import qualified Data.Vinyl as V+import Data.Vinyl.Functor ( Compose(..)+ , (:.)+ )+import qualified Frames as F+import qualified Frames.CSV as F+import qualified Frames.Folds as FF+import qualified Frames.Folds.Maybe as FFM+import qualified Frames.MapReduce as FMR+import qualified Frames.MapReduce.Maybe as FMRM import System.Random ( newStdGen , randomRs )+ -- Create types for the cols type Label = "label" F.:-> T.Text type Y = "y" F.:-> Double type X = "x" F.:-> Double-type AllCols = [Label,Y,X]+type AllCols = '[Label, Y, X] -- filter, leaving only rows with labels 'A', 'B' or 'C' unpack = FMR.unpackFilterOnField @Label (`elem` ["A", "B", "C"])@@ -34,18 +41,55 @@ -- put it all together: filter, group by label, sum the data cols and re-attach the key. -- Then turn the resulting list of Frames (each with only one Record in this case) -- into one Frame via (<>).+ mrFold = FMR.concatFold $ FMR.mapReduceFold unpack assign reduce ++-- Bleh, this should go in Frames. +instance (Eq (F.ElField a)) => Eq (Compose Maybe F.ElField a) where+ (==) (Compose fga) (Compose fga') = fga == fga'++instance (Ord (F.ElField a)) => Ord (Compose Maybe F.ElField a) where+ compare (Compose fga) (Compose fga') = fga `compare` fga'++unpack'+ :: FMR.Unpack (F.Rec (Maybe :. F.ElField) rs) (F.Rec (Maybe :. F.ElField) rs)+unpack' = FMRM.unpackNoOp++assign'+ :: FMR.Assign+ (F.Rec (Maybe :. F.ElField) '[Label])+ (F.Rec (Maybe :. F.ElField) '[Label, X, Y])+ (F.Rec (Maybe :. F.ElField) '[X, Y])+assign' = FMRM.splitOnKeys @'[Label]++reduce'+ :: FMR.Reduce+ (F.Rec (Maybe :. F.ElField) '[Label])+ (F.Rec (Maybe :. F.ElField) '[X, Y])+ (F.Rec (Maybe :. F.ElField) '[Label, X, Y])+reduce' = FMRM.foldAndAddKey $ (FFM.foldAllConstrained @Num @'[X, Y]) FL.sum++mrFold'+ :: FMR.Fold+ (F.Rec (Maybe :. F.ElField) '[Label, X, Y])+ [F.Rec (Maybe :. F.ElField) '[Label, X, Y]]+mrFold' = FMR.mapReduceFold unpack' assign' reduce'+ main :: IO () main = do f <- createFrame 1000 let result = FMR.fold mrFold f putStrLn $ (L.intercalate "\n" $ fmap show $ FL.fold FL.list result)+ let result' = FMR.fold mrFold' createHolyRows+ putStrLn . unlines . fmap show $ FL.fold FL.list result' {- Output-{label :-> "A", y :-> 1293.6893073755323, x :-> 1386.4314446405742}-{label :-> "B", y :-> 1940.9402110282622, x :-> 2244.645291592506}-{label :-> "C", y :-> 2009.8541388288395, x :-> 2128.7190606123568}+{label :-> "A", y :-> 1577.3965303339942, x :-> 1507.286289962377}+{label :-> "B", y :-> 1934.223021597267, x :-> 2135.9312483902577}+{label :-> "C", y :-> 1528.6898777108415, x :-> 1810.5096765228654}+{Just label :-> "A", Just x :-> 5.0, Just y :-> 2.0}+{Just label :-> "Z", Just x :-> 5.0, Just y :-> 9.0} -} --- create the Frame@@ -60,3 +104,14 @@ F.&: (randDbls !! (n + m)) F.&: V.RNil return $ F.toFrame $ fmap oneRow [0 .. (n - 1)]++createHolyRows :: [F.Rec (Maybe F.:. F.ElField) '[Label, X, Y]]+createHolyRows = fmap go [one, two, three, four]+ where+ go =+ V.rmap (either (const (Compose Nothing)) (Compose . Just) . getCompose)+ . F.readRec+ one = ["A", "1", "2"]+ two = ["Z", "NaN", "3"]+ three = ["A", "4", "lol"]+ four = ["Z", "5", "6"]
src/Frames/Folds.hs view
@@ -18,7 +18,7 @@ {-# LANGUAGE UndecidableSuperClasses #-} {-# OPTIONS_GHC -fwarn-incomplete-patterns #-} {-|-Module : Frames.Folds+Module : Frames.Monomorphic.Folds Description : Types and functions to simplify folding over Vinyl/Frames records. Leans heavily on the foldl package. Copyright : (c) Adam Conner-Sax 2019 License : BSD@@ -34,10 +34,12 @@ -- ** Types to act as "interpretation functors" for records of folds , FoldEndo(..)+ , FoldFieldEndo(..) , FoldRecord(..) -- * functions for building records of folds , recFieldF+ , fieldToFieldFold -- * functions for turning records of folds into folds of records , sequenceRecFold@@ -47,6 +49,10 @@ , foldAll , foldAllConstrained , foldAllMonoid++ -- * for generalizing+ , monoidWrapperToFold+ , MonoidalField ) where
+ src/Frames/Folds/General.hs view
@@ -0,0 +1,277 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses #-}+{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}+{-|+Module : Frames.Folds.General+Description : Types and functions to simplify folding over Vinyl/Frames records. Leans heavily on the foldl package. +Copyright : (c) Adam Conner-Sax 2019+License : BSD+Maintainer : adam_conner_sax@yahoo.com+Stability : experimental++Frames.Folds contains various helper functions designed to simplify folding over Frames/Vinyl records given some way of folding over each column.+-}+module Frames.Folds.General+ (+ -- * Types+ EndoFold++ -- ** Types to act as "interpretation functors" for records of folds+ , FoldEndo(..)+ , FoldRecord(..)++ -- * classes+ , EndoFieldFoldsToRecordFolds+ , ConstrainedField++ -- * functions for building records of folds+ , recFieldF+ , fieldToFieldFold++ -- * functions for turning records of folds into folds of records+ , sequenceRecFold+ , sequenceEndoFolds++ -- * functions using constraints to extend an endo-fold across a record+ , foldAll+ , foldAllConstrained+ , functorFoldAllConstrained+ , foldAllMonoid+ )+where++import Frames.MapReduce.General ( RecGetFieldC(..)+ , RCastC(..)+ , IsoRec(..)+ )+import Frames.Folds ( EndoFold+ , FoldFieldEndo(..)+ , monoidWrapperToFold+ , MonoidalField+ )++import qualified Control.Foldl as FL++import qualified Data.Profunctor as P+import qualified Data.Vinyl as V+import Data.Vinyl ( ElField )+import qualified Data.Vinyl.TypeLevel as V+import qualified Data.Vinyl.Functor as V+import qualified Frames as F+import Frames ( (:.) )+import qualified Frames.Melt as F+++-- | Turn and EndoFold (Maybe a) into an EndoFold ((Maybe :. ElField) '(s, a))+fieldFold+ :: (Functor f, V.KnownField t)+ => EndoFold (f (V.Snd t))+ -> EndoFold ((f :. ElField) t)+fieldFold =+ P.dimap (fmap (\(V.Field x) -> x) . V.getCompose) (V.Compose . fmap V.Field)+{-# INLINABLE fieldFold #-}++-- | Wrapper for Endo-folds of the field types of ElFields+newtype FoldEndo f t = FoldEndo { unFoldEndo :: EndoFold (f (V.Snd t)) }++-- | Wrapper for folds from a record to an interpreted field. Usually g ~ ElField+newtype FoldRecord record f g rs a = FoldRecord { unFoldRecord :: FL.Fold (record (f :. ElField) rs) (g a) }++-- | Control.Foldl helper for filtering+filteredFold :: (f a -> Maybe a) -> FL.Fold a b -> FL.Fold (f a) b+filteredFold toMaybe (FL.Fold step begin done) = FL.Fold step' begin done+ where step' x = maybe x (step x) . toMaybe++-- | Helper for building a 'FoldRecord' from a given fold and function of the record+recFieldF+ :: forall t rs a record f+ . (V.KnownField t, Applicative f)+ => (forall x . f x -> Maybe x)+ -> FL.Fold a (V.Snd t) -- ^ A fold from some type a to the field type of an ElField+ -> (record (f :. ElField) rs -> f a)+ -> FoldRecord record f (f :. ElField) rs t -- ^ the resulting 'FoldRecord'-wrapped fold +recFieldF toMaybe fld fromRecF = FoldRecord+ $ P.dimap fromRecF (V.Compose . pure . V.Field) (filteredFold toMaybe fld)+{-# INLINABLE recFieldF #-}++-- | special case of 'recFieldF' for the case when the function from the record to the folded type+-- is just retrieving the value in a field.+fieldToFieldFold+ :: forall x y rs record f+ . ( V.KnownField x+ , V.KnownField y+ , F.ElemOf rs x+ , RecGetFieldC x record f rs+ , Applicative f+ )+ => (forall z . f z -> Maybe z)+ -> FL.Fold (V.Snd x) (V.Snd y) -- ^ the fold to be wrapped+ -> FoldRecord record f (f :. ElField) rs y -- ^ the wrapped fold+fieldToFieldFold toMaybe fld = recFieldF toMaybe fld (rgetFieldF @x)+{-# INLINABLE fieldToFieldFold #-}++-- | Expand a record of folds, each from the entire record to one field, into a record of folds each from a larger record to the smaller one.+expandFoldInRecord+ :: forall rs as record f+ . (RCastC as rs record f, V.RMap as)+ => V.Rec (FoldRecord record f (f :. ElField) as) as -- ^ original fold + -> V.Rec (FoldRecord record f (f :. ElField) rs) as -- ^ resulting fold +expandFoldInRecord = V.rmap (FoldRecord . FL.premap rcastF . unFoldRecord)+{-# INLINABLE expandFoldInRecord #-}++-- | Change a record of single field folds to a record of folds from the entire record to each field+class EndoFieldFoldsToRecordFolds rs record f where+ endoFieldFoldsToRecordFolds :: F.Rec (FoldFieldEndo (f :. ElField)) rs -> F.Rec (FoldRecord record f (f :. ElField) rs) rs+++instance EndoFieldFoldsToRecordFolds '[] record f where+ endoFieldFoldsToRecordFolds _ = V.RNil+ {-# INLINABLE endoFieldFoldsToRecordFolds #-}++instance (EndoFieldFoldsToRecordFolds rs record f+ , RCastC rs (r ': rs) record f+ , V.KnownField r+ , RecGetFieldC r record f (r ': rs)+ , V.RMap rs+ ) => EndoFieldFoldsToRecordFolds (r ': rs) record f where+ endoFieldFoldsToRecordFolds (fe V.:& fes) = FoldRecord (FL.premap (rgetF @r) (unFoldFieldEndo fe)) V.:& expandFoldInRecord @(r ': rs) (endoFieldFoldsToRecordFolds fes)+ {-# INLINABLE endoFieldFoldsToRecordFolds #-}++-- can we do all/some of this via F.Rec (Fold as) bs?+-- | Turn a Record of folds into a fold over records+sequenceRecFold+ :: forall as rs record f+ . (IsoRec rs record f)+ => F.Rec (FoldRecord record f (f :. ElField) as) rs+ -> FL.Fold (record (f :. ElField) as) (record (f :. ElField) rs)+sequenceRecFold = fmap fromRec . V.rtraverse unFoldRecord+{-# INLINABLE sequenceRecFold #-}++-- | turn a record of folds over each field, into a fold over records +sequenceFieldEndoFolds+ :: (EndoFieldFoldsToRecordFolds rs record f, IsoRec rs record f)+ => F.Rec (FoldFieldEndo (f :. ElField)) rs+ -> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)+sequenceFieldEndoFolds = sequenceRecFold . endoFieldFoldsToRecordFolds+{-# INLINABLE sequenceFieldEndoFolds #-}++liftFold+ :: (V.KnownField t, Functor f)+ => FL.Fold (f (V.Snd t)) (f (V.Snd t))+ -> FoldFieldEndo (f :. ElField) t+liftFold = FoldFieldEndo . fieldFold+{-# INLINABLE liftFold #-}++-- This is not a natural transformation, FoldEndoT ~> FoldEndo F.EField, because of the constraint+liftFoldEndo+ :: (V.KnownField t, Functor f)+ => FoldEndo f t+ -> FoldFieldEndo (f :. ElField) t+liftFoldEndo = FoldFieldEndo . fieldFold . unFoldEndo+{-# INLINABLE liftFoldEndo #-}++liftFolds+ :: (V.RPureConstrained V.KnownField rs, V.RApply rs, Functor f)+ => F.Rec (FoldEndo f) rs+ -> F.Rec (FoldFieldEndo (f :. ElField)) rs+liftFolds = V.rapply liftedFs+ where liftedFs = V.rpureConstrained @V.KnownField $ V.Lift liftFoldEndo+{-# INLINABLE liftFolds #-}++-- | turn a record of endo-folds over each field, into a fold over records +sequenceEndoFolds+ :: forall rs record f+ . ( V.RApply rs+ , V.RPureConstrained V.KnownField rs+ , EndoFieldFoldsToRecordFolds rs record f+ , IsoRec rs record f+ , Functor f+ )+ => F.Rec (FoldEndo f) rs+ -> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)+sequenceEndoFolds = sequenceFieldEndoFolds . liftFolds+{-# INLINABLE sequenceEndoFolds #-}++-- | apply an unconstrained endo-fold, e.g., a fold which takes the last item in a container, to every field in a record+foldAll+ :: ( V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , EndoFieldFoldsToRecordFolds rs record f+ , IsoRec rs record f+ , Functor f+ )+ => (forall a . FL.Fold a a)+ -> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)+foldAll f = sequenceEndoFolds $ V.rpureConstrained @V.KnownField (FoldEndo f)+{-# INLINABLE foldAll #-}++class (c (V.Snd t)) => ConstrainedField c t+instance (c (V.Snd t)) => ConstrainedField c t++-- | Apply a constrained endo-fold to all fields of a record.+-- May require a use of TypeApplications, e.g., foldAllConstrained @Num FL.sum+foldAllConstrained+ :: forall c rs record f+ . ( V.RPureConstrained (ConstrainedField c) rs+ , V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , EndoFieldFoldsToRecordFolds rs record f+ , IsoRec rs record f+ , Applicative f+ )+ => (forall a . f a -> Maybe a)+ -> (forall a . c a => FL.Fold a a)+ -> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)+foldAllConstrained toMaybe f =+ sequenceEndoFolds $ V.rpureConstrained @(ConstrainedField c)+ (FoldEndo (fmap pure $ filteredFold toMaybe f))+{-# INLINABLE foldAllConstrained #-}++functorFoldAllConstrained+ :: forall c rs record f+ . ( V.RPureConstrained (ConstrainedField c) rs+ , V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , EndoFieldFoldsToRecordFolds rs record f+ , IsoRec rs record f+ , Applicative f+ )+ => (forall a . c a => FL.Fold (f a) (f a))+ -> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)+functorFoldAllConstrained f =+ sequenceEndoFolds $ V.rpureConstrained @(ConstrainedField c) (FoldEndo f)+{-# INLINABLE functorFoldAllConstrained #-}++-- | Given a monoid-wrapper, e.g., Sum, apply the derived endo-fold to all fields of a record+-- This is strictly less powerful than foldAllConstrained but might be simpler to use in some cases+foldAllMonoid+ :: forall g rs record f+ . ( V.RPureConstrained (ConstrainedField (MonoidalField g)) rs+ , V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , EndoFieldFoldsToRecordFolds rs record f+ , IsoRec rs record f+ , Applicative f+ )+ => (forall a . f a -> Maybe a)+ -> FL.Fold (record (f :. ElField) rs) (record (f :. ElField) rs)+foldAllMonoid toMaybe =+ foldAllConstrained @(MonoidalField g) toMaybe $ monoidWrapperToFold @g+{-# INLINABLE foldAllMonoid #-}+
+ src/Frames/Folds/Maybe.hs view
@@ -0,0 +1,177 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses #-}+{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}+{-|+Module : Frames.Folds.Maybe+Description : Types and functions to simplify folding over Vinyl/Frames records. Leans heavily on the foldl package. +Copyright : (c) Adam Conner-Sax 2019+License : BSD+Maintainer : adam_conner_sax@yahoo.com+Stability : experimental++Frames.Folds contains various helper functions designed to simplify folding over Frames/Vinyl records given some way of folding over each column.+-}+module Frames.Folds.Maybe+ (+ -- * Types+ EndoFold++ -- ** Types to act as "interpretation functors" for records of folds+ , FoldEndo(..)+ , FoldRecord(..)++ -- * functions for building records of folds+ , recFieldF+ , fieldToFieldFold++ -- * functions for turning records of folds into folds of records+ , sequenceRecFold+ , sequenceEndoFolds++ -- * functions using constraints to extend an endo-fold across a record+ , foldAll+ , foldAllConstrained+ , maybeFoldAllConstrained+ , foldAllMonoid+ )+where++import qualified Frames.MapReduce.General as MG+import qualified Frames.Folds.General as FG+import Frames.Folds.General ( FoldEndo(..)+ , FoldRecord(..)+ )++import Frames.Folds ( EndoFold+ , FoldFieldEndo(..)+ , monoidWrapperToFold+ , MonoidalField+ )++import qualified Control.Foldl as FL++import qualified Data.Profunctor as P+import qualified Data.Vinyl as V+import Data.Vinyl ( ElField )+import qualified Data.Vinyl.TypeLevel as V+import qualified Data.Vinyl.Functor as V+import qualified Frames as F+import Frames ( (:.) )+import qualified Frames.Melt as F++-- | Helper for building a 'FoldRecord' from a given fold and function of the record+recFieldF+ :: forall t rs a record+ . V.KnownField t+ => FL.Fold a (V.Snd t) -- ^ A fold from some type a to the field type of an ElField + -> (record (Maybe :. ElField) rs -> Maybe a) -- ^ a function to get the a value from the input record+ -> FG.FoldRecord record Maybe (Maybe :. ElField) rs t -- ^ the resulting 'FoldRecord'-wrapped fold +recFieldF = FG.recFieldF id+{-# INLINABLE recFieldF #-}++-- | special case of 'recFieldF' for the case when the function from the record to the folded type+-- is just retrieving the value in a field.+fieldToFieldFold+ :: forall x y rs record+ . ( V.KnownField x+ , V.KnownField y+ , F.ElemOf rs x+ , MG.RecGetFieldC x record Maybe rs+ )+ => FL.Fold (V.Snd x) (V.Snd y) -- ^ the fold to be wrapped+ -> FG.FoldRecord record Maybe (Maybe :. ElField) rs y -- ^ the wrapped fold+fieldToFieldFold = FG.fieldToFieldFold @x id+{-# INLINABLE fieldToFieldFold #-}++-- can we do all/some of this via F.Rec (Fold as) bs?+-- | Turn a Record of folds into a fold over records+sequenceRecFold+ :: forall as rs record+ . (MG.IsoRec rs record Maybe)+ => F.Rec (FG.FoldRecord record Maybe (Maybe :. ElField) as) rs+ -> FL.Fold (record (Maybe :. ElField) as) (record (Maybe :. ElField) rs)+sequenceRecFold = FG.sequenceRecFold --V.rtraverse unFoldRecord+{-# INLINABLE sequenceRecFold #-}++-- | turn a record of endo-folds over each field, into a fold over records +sequenceEndoFolds+ :: forall rs record+ . ( V.RApply rs+ , V.RPureConstrained V.KnownField rs+ , FG.EndoFieldFoldsToRecordFolds rs record Maybe+ , MG.IsoRec rs record Maybe+ )+ => F.Rec (FG.FoldEndo Maybe) rs+ -> FL.Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)+sequenceEndoFolds = FG.sequenceEndoFolds --sequenceFieldEndoFolds . liftFolds+{-# INLINABLE sequenceEndoFolds #-}++-- | apply an unconstrained endo-fold, e.g., a fold which takes the last item in a container, to every field in a record+foldAll+ :: ( V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , FG.EndoFieldFoldsToRecordFolds rs record Maybe+ , MG.IsoRec rs record Maybe+ )+ => (forall a . FL.Fold a a)+ -> FL.Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)+foldAll = FG.foldAll+{-# INLINABLE foldAll #-}++-- | Apply a constrained endo-fold to all fields of a record.+-- May require a use of TypeApplications, e.g., foldAllConstrained @Num FL.sum+foldAllConstrained+ :: forall c rs record+ . ( V.RPureConstrained (FG.ConstrainedField c) rs+ , V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , FG.EndoFieldFoldsToRecordFolds rs record Maybe+ , MG.IsoRec rs record Maybe+ )+ => (forall a . c a => FL.Fold a a)+ -> FL.Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)+foldAllConstrained = FG.foldAllConstrained @c id+{-# INLINABLE foldAllConstrained #-}++maybeFoldAllConstrained+ :: forall c rs record+ . ( V.RPureConstrained (FG.ConstrainedField c) rs+ , V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , FG.EndoFieldFoldsToRecordFolds rs record Maybe+ , MG.IsoRec rs record Maybe+ )+ => (forall a . c a => FL.Fold (Maybe a) (Maybe a))+ -> FL.Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)+maybeFoldAllConstrained = FG.functorFoldAllConstrained @c+{-# INLINABLE maybeFoldAllConstrained #-}++-- | Given a monoid-wrapper, e.g., Sum, apply the derived endo-fold to all fields of a record+-- This is strictly less powerful than foldAllConstrained but might be simpler to use in some cases+foldAllMonoid+ :: forall g rs record+ . ( V.RPureConstrained (FG.ConstrainedField (MonoidalField g)) rs+ , V.RPureConstrained V.KnownField rs+ , V.RApply rs+ , FG.EndoFieldFoldsToRecordFolds rs record Maybe+ , MG.IsoRec rs record Maybe+ )+ => FL.Fold (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)+foldAllMonoid = FG.foldAllMonoid @g id+{-# INLINABLE foldAllMonoid #-}
src/Frames/MapReduce.hs view
@@ -18,8 +18,8 @@ {-# LANGUAGE InstanceSigs #-} {-# OPTIONS_GHC -fwarn-incomplete-patterns #-} {-|-Module : Frames.MapReduce-Description : Helpers for using the map-reduce-folds package with Frames+Module : Frames.Monomorphic.MapReduce+Description : Helpers for using the map-reduce-folds package with Frames. Monomorphic in record and interpretation functor. Copyright : (c) Adam Conner-Sax 2019 License : BSD-3-Clause Maintainer : adam_conner_sax@yahoo.com@@ -68,6 +68,8 @@ import qualified Frames.InCore as FI import qualified Data.Vinyl as V import qualified Data.Vinyl.TypeLevel as V+import qualified Data.Vinyl.Functor as V+import Data.Coerce ( coerce ) -- | This is only here so we can use hash maps for the grouping step. This should properly be in Frames itself. instance Hash.Hashable (F.Record '[]) where
+ src/Frames/MapReduce/General.hs view
@@ -0,0 +1,258 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}+module Frames.MapReduce.General where++import qualified Control.MapReduce as MR+import Control.MapReduce -- for re-export++import qualified Control.Foldl as FL+import qualified Data.Foldable as F+import qualified Data.Hashable as Hash+import qualified Data.List as L+import Data.Maybe ( isJust )+import Data.Monoid ( Monoid(..) )+import Data.Hashable ( Hashable )+import Data.Kind ( Type )+import GHC.TypeLits ( Symbol )++import qualified Frames as F+import Frames ( (:.) )+import qualified Frames.Melt as F+import qualified Frames.InCore as FI+import qualified Data.Vinyl as V+import Data.Vinyl ( ElField )+import qualified Data.Vinyl.Functor as V+import qualified Data.Vinyl.TypeLevel as V+import qualified Data.Vinyl.SRec as V+import qualified Data.Vinyl.ARec as V+import qualified Foreign.Storable as FS++class RecGetFieldC t record f rs where+ rgetF :: ( V.KnownField t+ , F.ElemOf rs t+ ) => record (f :. ElField) rs -> (f :. ElField) t+ rgetFieldF :: ( V.KnownField t+ , Functor f+ , F.ElemOf rs t+ ) => record (f :. ElField) rs -> f (V.Snd t)+ rgetFieldF = fmap V.getField . V.getCompose . rgetF @t @record @f @rs++instance RecGetFieldC t V.Rec f rs where+ rgetF = V.rget @t++instance RecGetFieldC t V.ARec f rs where+ rgetF = V.aget @t++instance (V.FieldOffset (f :. ElField) rs t) => RecGetFieldC t V.SRec f rs where+ rgetF = V.sget @_ @t . V.getSRecNT --srecGetField @t @f @rs++class RCastC rs ss record f where+ rcastF :: record (f :. ElField) ss -> record (f :. ElField) rs++instance V.RecSubset V.Rec rs ss (V.RImage rs ss) => RCastC rs ss V.Rec f where+ rcastF = V.rcast++instance (V.IndexWitnesses (V.RImage rs ss), V.NatToInt (V.RLength rs)) => RCastC rs ss V.ARec f where+ rcastF = V.arecGetSubset++instance (V.RPureConstrained (V.FieldOffset (f :. ElField) ss) rs+ , V.RPureConstrained (V.FieldOffset (f :. ElField) rs) rs+ , V.RFoldMap rs+ , V.RMap rs+ , V.RApply rs+ , FS.Storable (V.Rec (f :. ElField) rs)) => RCastC rs ss V.SRec f where+ rcastF = V.SRecNT . V.srecGetSubset . V.getSRecNT++class IsoRec rs record f where+ toRec :: record (f :. ElField) rs -> V.Rec (f :. ElField) rs+ fromRec :: V.Rec (f :. ElField) rs -> record (f :. ElField) rs++instance IsoRec rs V.Rec f where+ toRec = id+ fromRec = id++instance FS.Storable (V.Rec (f :. ElField) rs) => IsoRec rs V.SRec f where+ toRec = V.fromSRec+ fromRec = V.toSRec+++instance (V.NatToInt (V.RLength rs)+ , V.RecApplicative rs+ , V.RPureConstrained (V.IndexableField rs) rs) => IsoRec rs V.ARec f where+ toRec = V.fromARec+ fromRec = V.toARec++-- | This is only here so we can use hash maps for the grouping step. This should properly be in Frames itself.+instance Hash.Hashable (record (f :. ElField) '[]) where+ hash = const 0+ {-# INLINABLE hash #-}+ hashWithSalt s = const s -- TODO: this seems BAD! Or not?+ {-# INLINABLE hashWithSalt #-}++instance (V.KnownField t+ , Functor f+ , RecGetFieldC t record f (t ': rs)+ , RCastC rs (t ': rs) record f+ , Hash.Hashable (f (V.Snd t))+ , Hash.Hashable (record (f :. ElField) rs)+ ) => Hash.Hashable (record (f :. ElField) (t ': rs)) where+ hashWithSalt s r = s `Hash.hashWithSalt` (rgetFieldF @t r) `Hash.hashWithSalt` (rcastF @rs r)+ {-# INLINABLE hashWithSalt #-}++-- | Don't do anything +unpackNoOp :: MR.Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)+unpackNoOp = MR.Filter (const True)++-- | Filter records using a function on the entire record. +unpackFilterRow+ :: (record (f :. ElField) rs -> Bool)+ -> MR.Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)+unpackFilterRow test = MR.Filter test++-- | Filter records based on a condition on only one field in the row. Will usually require a Type Application to indicate which field.+unpackFilterOnField+ :: forall t rs record f+ . (Functor f, V.KnownField t, F.ElemOf rs t, RecGetFieldC t record f rs)+ => (f (V.Snd t) -> Bool)+ -> MR.Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)+unpackFilterOnField test = unpackFilterRow (test . rgetFieldF @t)++unpackFilterOnGoodField+ :: forall t rs record f+ . (Functor f, V.KnownField t, F.ElemOf rs t, RecGetFieldC t record f rs)+ => (forall a . f a -> Maybe a)+ -> (V.Snd t -> Bool)+ -> MR.Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)+unpackFilterOnGoodField toMaybe testValue =+ let test' = (maybe False testValue) . toMaybe in unpackFilterOnField @t test'++-- | An unpack step which specifies a subset of columns, cs,+-- (via a type-application) and then filters a @record (Maybe :. Elfield) rs@+-- to only rows which have all good data in that subset.+unpackGoodRows+ :: forall cs rs record f+ . (RCastC cs rs record f)+ => (record (f :. ElField) cs -> Bool)+ -> MR.Unpack (record (f :. ElField) rs) (record (f :. ElField) rs)+unpackGoodRows testSubset = unpackFilterRow (testSubset . rcastF @cs)++-- | Assign both keys and data cols. Uses type applications to specify them if they cannot be inferred.+-- Keys usually can't. Data sometimes can.+assignKeysAndData+ :: forall ks cs rs record f+ . (RCastC ks rs record f, RCastC cs rs record f)+ => MR.Assign+ (record (f :. ElField) ks)+ (record (f :. ElField) rs)+ (record (f :. ElField) cs)+assignKeysAndData = MR.assign (rcastF @ks) (rcastF @cs)+{-# INLINABLE assignKeysAndData #-}++-- | Assign keys and leave the rest of the columns, excluding the keys, in the data passed to reduce.+splitOnKeys+ :: forall ks rs cs record f+ . (RCastC ks rs record f, RCastC cs rs record f, cs ~ F.RDeleteAll ks rs)+ => MR.Assign+ (record (f :. ElField) ks)+ (record (f :. ElField) rs)+ (record (f :. ElField) cs)+splitOnKeys = assignKeysAndData @ks @cs+{-# INLINABLE splitOnKeys #-}++-- | Assign keys and leave all columns, including the keys, in the data passed to reduce.+assignKeys+ :: forall ks rs record f+ . (RCastC ks rs record f)+ => MR.Assign+ (record (f :. ElField) ks)+ (record (f :. ElField) rs)+ (record (f :. ElField) rs)+assignKeys = MR.assign (rcastF @ks) id+{-# INLINABLE assignKeys #-}++-- | Reduce the data to a single row and then re-attach the key.+-- | NB: for all but Rec case, this will have to convert record to Rec and back for the append+reduceAndAddKey+ :: forall ks cs x record f+ . (IsoRec ks record f, IsoRec cs record f, IsoRec (ks V.++ cs) record f)+ => (forall h . Foldable h => h x -> record (f :. ElField) cs) -- ^ reduction step+ -> MR.Reduce+ (record (f :. ElField) ks)+ x+ (record (f :. ElField) (ks V.++ cs))+reduceAndAddKey process =+ MR.processAndLabel process (\k y -> fromRec (toRec k `V.rappend` toRec y))+{-# INLINABLE reduceAndAddKey #-}++-- | Reduce by folding the data to a single row and then re-attaching the key.+foldAndAddKey+ :: (IsoRec ks record f, IsoRec cs record f, IsoRec (ks V.++ cs) record f)+ => FL.Fold x (record (f :. ElField) cs) -- ^ reduction fold+ -> MR.Reduce+ (record (f :. ElField) ks)+ x+ (record (f :. ElField) (ks V.++ cs))+foldAndAddKey fld =+ MR.foldAndLabel fld (\k y -> fromRec (toRec k `V.rappend` toRec y))+{-# INLINABLE foldAndAddKey #-}++-- | Transform a reduce which produces a container of results, with a function from each result to a record,+-- into a reduce which produces a FrameRec of the result records with the key re-attached.+makeRecsWithKey+ :: ( Functor g+ , Foldable g+ , IsoRec ks record f+ , IsoRec as record f+ , IsoRec (ks V.++ as) record f+ )+ => (y -> record (f :. ElField) as) -- ^ map a result to a record+ -> MR.Reduce (record (f :. ElField) ks) x (g y) -- ^ original reduce+ -> MR.Reduce+ (record (f :. ElField) ks)+ x+ (g (record (f :. ElField) (ks V.++ as)))+makeRecsWithKey makeRec reduceToY = MR.reduceMapWithKey addKey reduceToY+ where+ addKey k = fmap (\y -> fromRec . V.rappend (toRec k) . toRec $ makeRec y)+{-# INLINABLE makeRecsWithKey #-}++-- | Transform an effectful reduce which produces a container of results, with a function from each result to a record,+-- into a reduce which produces a FrameRec of the result records with the key re-attached.+makeRecsWithKeyM+ :: ( Monad m+ , Functor g+ , Foldable g+ , IsoRec ks record f+ , IsoRec as record f+ , IsoRec (ks V.++ as) record f+ )+ => (y -> record (f :. ElField) as) -- ^ map a result to a record+ -> MR.ReduceM m (record (f :. ElField) ks) x (g y) -- ^ original reduce+ -> MR.ReduceM+ m+ (record (f :. ElField) ks)+ x+ (g (record (f :. ElField) (ks V.++ as)))+makeRecsWithKeyM makeRec reduceToY = MR.reduceMMapWithKey addKey reduceToY+ where+ addKey k = fmap (\y -> fromRec . V.rappend (toRec k) . toRec $ makeRec y)+{-# INLINABLE makeRecsWithKeyM #-}++
+ src/Frames/MapReduce/Maybe.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}+module Frames.MapReduce.Maybe+ ( module Frames.MapReduce.Maybe+-- , module Frames.MapReduce.General+ )+where+import qualified Control.MapReduce as MR+import Control.MapReduce -- for re-export+import qualified Frames.MapReduce.General as MG++import qualified Control.Foldl as FL+import qualified Data.Foldable as F+import qualified Data.Hashable as Hash+import qualified Data.List as L+import Data.Maybe ( isJust )+import Data.Monoid ( Monoid(..) )+import Data.Hashable ( Hashable )++import qualified Frames as F+import Frames ( (:.) )+import qualified Frames.Melt as F+import qualified Frames.InCore as FI+import qualified Data.Vinyl as V+import Data.Vinyl ( ElField )+import qualified Data.Vinyl.Functor as V+import qualified Data.Vinyl.TypeLevel as V++-- | Don't do anything +unpackNoOp+ :: MR.Unpack (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)+unpackNoOp = MG.unpackNoOp++-- | Filter records using a function on the entire record. +unpackFilterRow+ :: (record (Maybe :. ElField) rs -> Bool)+ -> MR.Unpack (record (Maybe :. ElField) rs) (record (Maybe :. ElField) rs)+unpackFilterRow = MG.unpackFilterRow++-- | Filter records based on a condition on only one field in the row. Will usually require a Type Application to indicate which field.+unpackFilterOnField+ :: forall t rs record+ . (V.KnownField t, F.ElemOf rs t, MG.RecGetFieldC t record Maybe rs)+ => (Maybe (V.Snd t) -> Bool)+ -> MR.Unpack+ (record (Maybe :. ElField) rs)+ (record (Maybe :. ElField) rs)+unpackFilterOnField = MG.unpackFilterOnField @t++-- | An unpack step which specifies a subset of columns, cs,+-- (via a type-application) and then filters a @Rec (Maybe :. Elfield) rs@+-- to only rows which have all good data in that subset.+unpackGoodRows+ :: forall cs rs record+ . (MG.RCastC cs rs record Maybe)+ => (record (Maybe :. ElField) cs -> Bool)+ -> MR.Unpack+ (record (Maybe :. ElField) rs)+ (record (Maybe :. ElField) rs)+unpackGoodRows = MG.unpackGoodRows --unpackFilterRow (isJust . F.recMaybe . F.rcast @cs)++unpackGoodRecRows+ :: forall cs rs record+ . (MG.RCastC cs rs V.Rec Maybe)+ => MR.Unpack (V.Rec (Maybe :. ElField) rs) (V.Rec (Maybe :. ElField) rs)+unpackGoodRecRows = MG.unpackGoodRows @cs (isJust . F.recMaybe)++-- | Assign both keys and data cols. Uses type applications to specify them if they cannot be inferred.+-- Keys usually can't. Data sometimes can.+assignKeysAndData+ :: forall ks cs rs record+ . (MG.RCastC ks rs record Maybe, MG.RCastC cs rs record Maybe)+ => MR.Assign+ (record (Maybe :. ElField) ks)+ (record (Maybe :. ElField) rs)+ (record (Maybe :. ElField) cs)+assignKeysAndData = MG.assignKeysAndData --MR.assign (F.rcast @ks) (F.rcast @cs)+{-# INLINABLE assignKeysAndData #-}++-- | Assign keys and leave the rest of the columns, excluding the keys, in the data passed to reduce.+splitOnKeys+ :: forall ks rs cs record+ . ( MG.RCastC ks rs record Maybe+ , MG.RCastC cs rs record Maybe+ , cs ~ F.RDeleteAll ks rs+ )+ => MR.Assign+ (record (Maybe :. ElField) ks)+ (record (Maybe :. ElField) rs)+ (record (Maybe :. ElField) cs)+splitOnKeys = assignKeysAndData @ks @cs+{-# INLINABLE splitOnKeys #-}++-- | Assign keys and leave all columns, including the keys, in the data passed to reduce.+assignKeys+ :: forall ks rs record+ . (MG.RCastC ks rs record Maybe)+ => MR.Assign+ (record (Maybe :. ElField) ks)+ (record (Maybe :. ElField) rs)+ (record (Maybe :. ElField) rs)+assignKeys = MG.assignKeys+{-# INLINABLE assignKeys #-}++-- | Reduce the data to a single row and then re-attach the key.+reduceAndAddKey+ :: forall ks cs x record+ . ( MG.IsoRec ks record Maybe+ , MG.IsoRec cs record Maybe+ , MG.IsoRec (ks V.++ cs) record Maybe+ , FI.RecVec ((ks V.++ cs))+ )+ => (forall h . Foldable h => h x -> record (Maybe :. ElField) cs) -- ^ reduction step+ -> MR.Reduce+ (record (Maybe :. ElField) ks)+ x+ (record (Maybe :. ElField) (ks V.++ cs))+reduceAndAddKey = MG.reduceAndAddKey+{-# INLINABLE reduceAndAddKey #-}++-- | Reduce by folding the data to a single row and then re-attaching the key.+foldAndAddKey+ :: ( MG.IsoRec ks record Maybe+ , MG.IsoRec cs record Maybe+ , MG.IsoRec (ks V.++ cs) record Maybe+ , FI.RecVec ((ks V.++ cs))+ )+ => FL.Fold x (record (Maybe :. ElField) cs) -- ^ reduction fold+ -> MR.Reduce+ (record (Maybe :. ElField) ks)+ x+ (record (Maybe :. ElField) (ks V.++ cs))+foldAndAddKey = MG.foldAndAddKey+{-# INLINABLE foldAndAddKey #-}++-- | Transform a reduce which produces a container of results, with a function from each result to a record,+-- into a reduce which produces a FrameRec of the result records with the key re-attached.+makeRecsWithKey+ :: ( Functor g+ , Foldable g+ , MG.IsoRec ks record Maybe+ , MG.IsoRec as record Maybe+ , MG.IsoRec (ks V.++ as) record Maybe+ , (FI.RecVec (ks V.++ as))+ )+ => (y -> record (Maybe :. ElField) as) -- ^ map a result to a record+ -> MR.Reduce (record (Maybe :. ElField) ks) x (g y) -- ^ original reduce+ -> MR.Reduce+ (record (Maybe :. ElField) ks)+ x+ (g (record (Maybe :. ElField) (ks V.++ as)))+makeRecsWithKey = MG.makeRecsWithKey+{-# INLINABLE makeRecsWithKey #-}++-- | Transform an effectful reduce which produces a container of results, with a function from each result to a record,+-- into a reduce which produces a FrameRec of the result records with the key re-attached.+makeRecsWithKeyM+ :: ( Monad m+ , Functor g+ , Foldable g+ , MG.IsoRec ks record Maybe+ , MG.IsoRec as record Maybe+ , MG.IsoRec (ks V.++ as) record Maybe+ , (FI.RecVec (ks V.++ as))+ )+ => (y -> record (Maybe :. ElField) as) -- ^ map a result to a record+ -> MR.ReduceM m (record (Maybe :. ElField) ks) x (g y) -- ^ original reduce+ -> MR.ReduceM+ m+ (record (Maybe :. ElField) ks)+ x+ (g (record (Maybe :. ElField) (ks V.++ as)))+makeRecsWithKeyM = MG.makeRecsWithKeyM+{-# INLINABLE makeRecsWithKeyM #-}