{-# 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.Proxy
import Data.Vinyl (recordToList, rmap, reifyConstraint, Dict(..), Rec)
import Data.Vinyl.TypeLevel (RecAll)
import Data.Vinyl.Functor (Identity(..), Const(..), Compose(..), (:.))
import Frames.Col
import Frames.RecF

-- | A record with unadorned values. This is @Vinyl@'s 'Rec'
-- 'Identity'. We give this type a name as it is used pervasively for
-- records in 'Frames'.
type Record = Rec Identity

-- | A @cons@ function for building 'Record' values.
(&:) :: a -> Record rs -> Record (s :-> a ': rs)
x &: xs = frameCons (Identity 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 (Identity x :& xs) = (x, xs)
recUncons x = case x of

-- | Undistribute 'Maybe' from a 'Rec' 'Maybe'. This is just a
-- specific usage of 'rtraverse', but it is quite common.
recMaybe :: Rec Maybe cs -> Maybe (Record cs)
recMaybe = rtraverse (fmap Identity)
{-# INLINE recMaybe #-}

-- | Show each field of a 'Record' /without/ its column name.
showFields :: (RecAll Identity (UnColumn ts) Show, AsVinyl ts)
           => Record ts -> [String]
showFields = recordToList . rmap aux . reifyConstraint p . toVinyl
  where p = Proxy :: Proxy Show
        aux :: (Dict Show :. Identity) a -> Const String a
        aux (Compose (Dict x)) = Const (show x)
{-# INLINABLE showFields #-}