content: {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE TypeApplications #-}
  {-# LANGUAGE ConstraintKinds,
               DataKinds,
               EmptyCase,
               FlexibleContexts,
               FlexibleInstances,
               FunctionalDependencies,
               KindSignatures,
               GADTs,
               MultiParamTypeClasses,
               PatternSynonyms,
               PolyKinds,
               ScopedTypeVariables,
               TypeFamilies,
               TypeOperators,
               UndecidableInstances #-}
  {-# OPTIONS_GHC -fno-warn-orphans #-}
  module Frames.Rec where
  import Data.Vinyl hiding (rget)
  import qualified Data.Vinyl as V
  import Data.Vinyl.Functor (Const(..), Compose(..), (:.))
  import Data.Vinyl.Class.Method (PayloadType)
  import Frames.Col
  import GHC.TypeLits (KnownSymbol)
  
  -- | A record with unadorned values. This is @Vinyl@'s 'Rec'
  -- 'ElField'. We give this type a name as it is used pervasively for
  -- records in 'Frames'.
  type Record = FieldRec
  
  -- | A @cons@ function for building 'Record' values.
  (&:) :: KnownSymbol s => a -> Record rs -> Record (s :-> a ': rs)
  x &: xs = Field x :& xs
  infixr 5 &:
  
  type family RecordColumns t where
    RecordColumns (Record ts) = ts
  
  -- | Separate the first element of a 'Record' from the rest of the row.
  recUncons :: Record (s :-> a ': rs) -> (a, Record rs)
  recUncons (Field x :& xs) = (x, xs)
  -- recUncons x = case x of _ -> error "recUncons impossible case"
  
  -- | Undistribute 'Maybe' from a 'Rec' 'Maybe'. This is just a
  -- specific usage of 'rtraverse', but it is quite common.
  recMaybe :: Rec (Maybe :. ElField) cs -> Maybe (Record cs)
  recMaybe = rtraverse getCompose
  {-# INLINE recMaybe #-}
  
  -- | Show each field of a 'Record' /without/ its column name.
  showFields :: (RecMapMethod Show ElField ts, RecordToList ts)
             => Record ts -> [String]
  showFields = recordToList . rmapMethod @Show aux
    where aux :: (Show (PayloadType ElField a)) => ElField a -> Const String a
          aux (Field x) = Const (show x)
  {-# INLINABLE showFields #-}
  
  -- | Get the value of a field of a 'Record'. This is intended for use
  -- with @TypeApplications@, as compared to 'rgetv' that is intended
  -- for use with @OverloadedLabels@.
  rgetField :: forall t s a rs. (t ~ '(s,a), t ∈ rs) => Record rs -> a
  rgetField = getField . V.rget @t
  {-# INLINE rgetField #-}
  
  -- | Replace the value of a field of a 'Record'. This is intended for
  -- use with @TypeApplications@, as compared to 'rputf' that is
  -- intended for use with @OverloadedLabels@.
  rputField :: forall t s a rs. (t ~ '(s,a), t ∈ rs, KnownSymbol s)
            => a -> Record rs -> Record rs
  rputField = V.rput @_ @t . Field
  {-# INLINE rputField #-}
  