content: {-# LANGUAGE AllowAmbiguousTypes,
               ConstraintKinds,
               CPP,
               DataKinds,
               FlexibleContexts,
               FlexibleInstances,
               GADTs,
               KindSignatures,
               MultiParamTypeClasses,
               PatternSynonyms,
               PolyKinds,
               RankNTypes,
               ScopedTypeVariables,
               TypeApplications,
               TypeFamilies,
               TypeOperators,
               ViewPatterns #-}
  module Frames.RecF (V.rappend, V.rtraverse, rdel, CanDelete,
                      -- frameCons, frameConsA, frameSnoc,
                      -- pattern (:&), pattern Nil,
                      AllCols,
                      UnColumn, -- AsVinyl(..)
                      StripFieldNames(..),
                      mapMono,
                      -- mapMethod,
                      -- runcurry, runcurry', runcurryA, runcurryA',
                      -- ShowRec,
                      -- showRec,
                      ColFun, ColumnHeaders,
                      columnHeaders) where
  -- import Data.List (intercalate)
  import Data.Kind (Type)
  import Data.Proxy
  import qualified Data.Vinyl as V
  import Data.Vinyl (Rec(..))
  import Data.Vinyl.Derived (StripFieldNames(..), Unlabeled)
  import Data.Vinyl.Functor ((:.))
  import Data.Vinyl.TypeLevel
  import Frames.Col
  import Frames.TypeLevel
  import GHC.TypeLits (Symbol, KnownSymbol, symbolVal)
  
  -- -- | Add a column to the head of a row.
  -- frameCons :: (Functor f, KnownSymbol s)
  --           => f a -> Rec f rs -> Rec f (s :-> a ': rs)
  -- frameCons = (V.:&) . fmap Col
  -- {-# INLINE frameCons #-}
  
  -- -- | Add a pure column to the head of a row.
  -- frameConsA :: (Applicative f, KnownSymbol s)
  --            => a -> Rec f rs -> Rec f (s :-> a ': rs)
  -- frameConsA = (V.:&) . fmap Col . pure
  -- {-# INLINE frameConsA #-}
  
  -- -- | Separate the first element of a row from the rest of the row.
  -- frameUncons :: Functor f => Rec f (s :-> r ': rs) -> (f r, Rec f rs)
  -- frameUncons (x V.:& xs) = (fmap getCol x, xs)
  -- {-# INLINE frameUncons #-}
  
  -- -- | Add a column to the tail of a row. Note that the supplied value
  -- -- should be a 'Col' to work with the @Frames@ tooling.
  -- frameSnoc :: Rec f rs -> f r -> Rec f (rs ++ '[r])
  -- frameSnoc r x = V.rappend r (x V.:& RNil)
  -- {-# INLINE frameSnoc #-}
  
  -- pattern Nil :: Rec f '[]
  -- pattern Nil <- RNil where
  --   Nil = RNil
  
  -- pattern (:&) :: (Functor f, KnownSymbol s) => f r -> Rec f rs -> Rec f (s :-> r ': rs)
  -- pattern x :& xs <- (frameUncons -> (x, xs)) where
  --   x :& xs = frameCons x xs
  
  class ColumnHeaders (cs::[(Symbol, Type)]) where
    -- | Return the column names for a record.
    columnHeaders :: proxy (Rec f cs) -> [String]
  
  instance ColumnHeaders '[] where
    columnHeaders _ = []
  
  instance forall cs s c. (ColumnHeaders cs, KnownSymbol s)
      => ColumnHeaders (s :-> c ': cs) where
    columnHeaders _ = symbolVal (Proxy::Proxy s) : columnHeaders (Proxy::Proxy (Rec f cs))
  
  -- | A type function to convert a 'Rec f' to a 'Rec (g :. f)'. @ColFun
  -- f (Record rs) = Rec (f :. ElField) rs@.
  type family ColFun f x where
    ColFun f (Rec g rs) = Rec (f :. g) rs
  
  -- | Strip the column information from each element of a list of
  -- types.
  type family UnColumn (ts :: [(Symbol, Type)]) where
    UnColumn '[] = '[]
    UnColumn ((s :-> t) ': ts) = t ': UnColumn ts
  
  -- | Enforce a constraint on the payload type of each column.
  type AllCols c ts = AllConstrained c (UnColumn ts)
  
  -- | Map a function across a 'Rec' with monomorphic (i.e. all the
  -- same) indices.
  mapMonoV :: (AllAre a ts, Functor f)
           => (a -> b)
           -> Rec f ts
           -> Rec f (ReplaceAll b ts)
  mapMonoV _ RNil = RNil
  mapMonoV f (x :& xs) = fmap f x :& mapMonoV f xs
  
  -- | Map a function across a homogeneous 'Rec' of named values. This
  -- is a thin wrapper over 'mapMonoV'.
  mapMono :: (AllAre a (Unlabeled ts),
              StripFieldNames ts,
              StripFieldNames (ReplaceAllSnd b ts),
              ReplaceAll b (Unlabeled ts) ~ Unlabeled (ReplaceAllSnd b ts))
          => (a -> b)
          -> Rec V.ElField ts
          -> Rec V.ElField (ReplaceAllSnd b ts)
  mapMono f = withNames . mapMonoV f . stripNames
  
  -- | A constraint that a field can be deleted from a record.
  type CanDelete r rs = (V.RElem r rs (RIndex r rs), RDelete r rs V.⊆ rs)
  
  -- | Delete a field from a record
  rdel :: CanDelete r rs => Rec f rs -> Rec f (RDelete r rs)
  rdel = V.rcast
  