{-# 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 #-}