diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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)
diff --git a/Frames-map-reduce.cabal b/Frames-map-reduce.cabal
--- a/Frames-map-reduce.cabal
+++ b/Frames-map-reduce.cabal
@@ -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
diff --git a/examples/AddRowsByLabel.hs b/examples/AddRowsByLabel.hs
--- a/examples/AddRowsByLabel.hs
+++ b/examples/AddRowsByLabel.hs
@@ -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"]
diff --git a/src/Frames/Folds.hs b/src/Frames/Folds.hs
--- a/src/Frames/Folds.hs
+++ b/src/Frames/Folds.hs
@@ -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
 
diff --git a/src/Frames/Folds/General.hs b/src/Frames/Folds/General.hs
new file mode 100644
--- /dev/null
+++ b/src/Frames/Folds/General.hs
@@ -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 #-}
+
diff --git a/src/Frames/Folds/Maybe.hs b/src/Frames/Folds/Maybe.hs
new file mode 100644
--- /dev/null
+++ b/src/Frames/Folds/Maybe.hs
@@ -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 #-}
diff --git a/src/Frames/MapReduce.hs b/src/Frames/MapReduce.hs
--- a/src/Frames/MapReduce.hs
+++ b/src/Frames/MapReduce.hs
@@ -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
diff --git a/src/Frames/MapReduce/General.hs b/src/Frames/MapReduce/General.hs
new file mode 100644
--- /dev/null
+++ b/src/Frames/MapReduce/General.hs
@@ -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 #-}
+
+
diff --git a/src/Frames/MapReduce/Maybe.hs b/src/Frames/MapReduce/Maybe.hs
new file mode 100644
--- /dev/null
+++ b/src/Frames/MapReduce/Maybe.hs
@@ -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 #-}
