module Composite.Record
( Rec((:&), RNil), Record
, pattern (:*:), pattern (:^:)
, (:->)(Val, getVal), valName, valWithName
, RElem, rlens, rlens'
, AllHave, HasInstances, ValuesAllHave
, zipRecsWith, reifyDicts, recordToNonEmpty
, ReifyNames(reifyNames)
, RecWithContext(rmapWithContext)
) where
import Control.Lens.TH (makeWrapped)
import Data.Functor.Identity (Identity(Identity))
import Data.Kind (Constraint)
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.Proxy (Proxy(Proxy))
import Data.Semigroup (Semigroup)
import Data.String (IsString)
import Data.Text (Text, pack)
import Data.Vinyl (Rec((:&), RNil), RecApplicative, recordToList, rpure)
import qualified Data.Vinyl as Vinyl
import Data.Vinyl.Functor (Compose(Compose), Const(Const), (:.))
import Data.Vinyl.Lens (type (∈))
import qualified Data.Vinyl.TypeLevel as Vinyl
import Foreign.Storable (Storable)
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
type Record = Rec Identity
type RElem r rs = Vinyl.RElem r rs (Vinyl.RIndex r rs)
newtype (:->) (s :: Symbol) a = Val { getVal :: a }
makeWrapped ''(:->)
deriving instance Bounded a => Bounded (s :-> a)
deriving instance Enum a => Enum (s :-> a)
deriving instance Eq a => Eq (s :-> a)
deriving instance Floating a => Floating (s :-> a)
deriving instance Fractional a => Fractional (s :-> a)
deriving instance Integral a => Integral (s :-> a)
deriving instance IsString a => IsString (s :-> a)
deriving instance Monoid a => Monoid (s :-> a)
deriving instance Num a => Num (s :-> a)
deriving instance Ord a => Ord (s :-> a)
deriving instance Real a => Real (s :-> a)
deriving instance RealFloat a => RealFloat (s :-> a)
deriving instance RealFrac a => RealFrac (s :-> a)
deriving instance Semigroup a => Semigroup (s :-> a)
deriving instance Storable a => Storable (s :-> a)
instance Functor ((:->) s) where
fmap f = Val . f . getVal
instance Applicative ((:->) s) where
pure = Val
Val f <*> Val a = Val (f a)
instance Foldable ((:->) s) where
foldr f z (Val a) = f a z
instance Traversable ((:->) s) where
traverse k (Val a) = Val <$> k a
instance Monad ((:->) s) where
return = Val
Val a >>= k = k a
instance forall (s :: Symbol) a. (KnownSymbol s, Show a) => Show (s :-> a) where
showsPrec p (Val a) = ((symbolVal (Proxy :: Proxy s) ++ " :-> ") ++) . showsPrec p a
valName :: forall s a. KnownSymbol s => s :-> a -> Text
valName _ = pack (symbolVal (Proxy :: Proxy s))
valWithName :: forall s a. KnownSymbol s => s :-> a -> (Text, a)
valWithName v = (valName v, getVal v)
pattern (:*:) :: () => () => a -> Rec Identity rs -> Rec Identity (s :-> a ': rs)
pattern (:*:) a rs = Identity (Val a) :& rs
infixr 5 :*:
pattern (:^:) :: Functor f => () => f a -> Rec f rs -> Rec f (s :-> a ': rs)
pattern (:^:) fa rs <- (fmap getVal -> fa) :& rs where
(:^:) fa rs = fmap Val fa :& rs
infixr 5 :^:
rlens :: (Functor g, RElem (s :-> a) rs, Functor g) => proxy (s :-> a) -> (a -> g a) -> Rec Identity rs -> g (Rec Identity rs)
rlens proxy f =
Vinyl.rlens proxy $ \ (Identity (Val a)) ->
Identity . Val <$> f a
rlens' :: (Functor f, Functor g, RElem (s :-> a) rs, Functor g) => proxy (s :-> a) -> (f a -> g (f a)) -> Rec f rs -> g (Rec f rs)
rlens' proxy f =
Vinyl.rlens proxy $ \ (fmap getVal -> fa) ->
fmap Val <$> f fa
zipRecsWith :: (forall a. f a -> g a -> h a) -> Rec f as -> Rec g as -> Rec h as
zipRecsWith _ RNil _ = RNil
zipRecsWith f (r :& rs) (s :& ss) = f r s :& zipRecsWith f rs ss
recordToNonEmpty :: Rec (Const a) (r ': rs) -> NonEmpty a
recordToNonEmpty (Const a :& rs) = a :| recordToList rs
type family HasInstances (a :: u) (cs :: [u -> Constraint]) :: Constraint where
HasInstances a '[] = ()
HasInstances a (c ': cs) = (c a, HasInstances a cs)
type family AllHave (cs :: [u -> Constraint]) (as :: [u]) :: Constraint where
AllHave cs '[] = ()
AllHave cs (a ': as) = (HasInstances a cs, AllHave cs as)
type family ValuesAllHave (cs :: [u -> Constraint]) (as :: [u]) :: Constraint where
ValuesAllHave cs '[] = ()
ValuesAllHave cs (s :-> a ': as) = (HasInstances a cs, ValuesAllHave cs as)
reifyDicts
:: forall (cs :: [u -> Constraint]) (f :: u -> *) (rs :: [u]) (proxy :: [u -> Constraint] -> *).
(AllHave cs rs, RecApplicative rs)
=> proxy cs
-> (forall proxy' (a :: u). HasInstances a cs => proxy' a -> f a)
-> Rec f rs
reifyDicts _ f = go (rpure (Const ()))
where
go :: forall (rs' :: [u]). AllHave cs rs' => Rec (Const ()) rs' -> Rec f rs'
go RNil = RNil
go ((_ :: Const () a) :& xs) = f (Proxy @a) :& go xs
class ReifyNames (rs :: [*]) where
reifyNames :: Rec f rs -> Rec ((,) Text :. f) rs
instance ReifyNames '[] where
reifyNames _ = RNil
instance forall (s :: Symbol) a (rs :: [*]). (KnownSymbol s, ReifyNames rs) => ReifyNames (s :-> a ': rs) where
reifyNames (fa :& rs) = Compose ((,) (pack $ symbolVal (Proxy @s)) fa) :& reifyNames rs
class RecWithContext (ss :: [*]) (ts :: [*]) where
rmapWithContext :: proxy ss -> (forall r. r ∈ ss => f r -> g r) -> Rec f ts -> Rec g ts
instance RecWithContext ss '[] where
rmapWithContext _ _ _ = RNil
instance forall r (ss :: [*]) (ts :: [*]). (r ∈ ss, RecWithContext ss ts) => RecWithContext ss (r ': ts) where
rmapWithContext proxy n (r :& rs) = n r :& rmapWithContext proxy n rs