{-# LANGUAGE AllowAmbiguousTypes, BangPatterns, CPP, ConstraintKinds,
DataKinds, EmptyCase, FlexibleContexts,
FlexibleInstances, GADTs, KindSignatures,
MultiParamTypeClasses, PolyKinds, RankNTypes,
ScopedTypeVariables, TypeApplications, TypeOperators,
UndecidableInstances #-}
module Data.Vinyl.CoRec where
import Data.Maybe(fromJust)
import Data.Vinyl.Core
import Data.Vinyl.Lens (RElem, rget, rput, type (∈))
import Data.Vinyl.Functor (Compose(..), (:.), Identity(..), Const(..))
import Data.Vinyl.TypeLevel
import Data.Vinyl.Derived (FieldType, (:::))
import GHC.TypeLits (Symbol, KnownSymbol)
import GHC.Types (type Type)
import Unsafe.Coerce (unsafeCoerce)
data CoRec :: (k -> *) -> [k] -> * where
CoRec :: RElem a ts (RIndex a ts) => !(f a) -> CoRec f ts
corec :: forall (l :: Symbol)
(ts :: [(Symbol,Type)])
(f :: (Symbol,Type) -> Type).
(KnownSymbol l, (l ::: FieldType l ts) ∈ ts)
=> f (l ::: FieldType l ts) -> CoRec f ts
corec x = CoRec x
foldCoRec :: (forall a. RElem a ts (RIndex a ts) => f a -> b) -> CoRec f ts -> b
foldCoRec f (CoRec x) = f x
type Field = CoRec Identity
newtype Op b a = Op { runOp :: a -> b }
class ShowF f a where
showf :: f a -> String
instance Show (f a) => ShowF f a where
showf = show
instance forall f ts. RPureConstrained (ShowF f) ts => Show (CoRec f ts) where
show x = "{|" ++ onCoRec @(ShowF f) showf x ++ "|}"
instance forall ts. (RecApplicative ts, RecordToList ts,
RApply ts, ReifyConstraint Eq Maybe ts, RMap ts)
=> Eq (CoRec Identity ts) where
crA == crB = and . recordToList
$ rzipWith f (toRec crA) (coRecToRec' crB)
where
f :: forall a. (Dict Eq :. Maybe) a -> Maybe a -> Const Bool a
f (Compose (Dict a)) b = Const $ a == b
toRec = reifyConstraint @Eq . coRecToRec'
coRecToRec :: forall f ts. RecApplicative ts
=> CoRec f ts -> Rec (Maybe :. f) ts
coRecToRec (CoRec x) = rput (Compose (Just x)) (rpure (Compose Nothing))
coRecToRec' :: (RecApplicative ts, RMap ts)
=> CoRec Identity ts -> Rec Maybe ts
coRecToRec' = rmap (fmap getIdentity . getCompose) . coRecToRec
class FoldRec ss ts where
foldRec :: (CoRec f ss -> CoRec f ss -> CoRec f ss)
-> CoRec f ss
-> Rec f ts
-> CoRec f ss
instance FoldRec ss '[] where foldRec _ z _ = z
instance (t ∈ ss, FoldRec ss ts) => FoldRec ss (t ': ts) where
foldRec f z (x :& xs) = foldRec f (f z (CoRec x)) xs
coRecMap :: (forall x. f x -> g x) -> CoRec f ts -> CoRec g ts
coRecMap nt (CoRec x) = CoRec (nt x)
getDict :: forall c ts a proxy. (a ∈ ts, RPureConstrained c ts)
=> proxy a -> DictOnly c a
getDict _ = rget @a (rpureConstrained @c @ts DictOnly)
coRecMapC :: forall c ts f g.
(RPureConstrained c ts)
=> (forall x. (x ∈ ts, c x) => f x -> g x)
-> CoRec f ts
-> CoRec g ts
coRecMapC nt (CoRec x) = case getDict @c @ts x of
DictOnly -> CoRec (nt x)
coRecTraverse :: Functor h
=> (forall x. f x -> h (g x)) -> CoRec f ts -> h (CoRec g ts)
coRecTraverse f (CoRec x) = fmap CoRec (f x)
foldRec1 :: FoldRec (t ': ts) ts
=> (CoRec f (t ': ts) -> CoRec f (t ': ts) -> CoRec f (t ': ts))
-> Rec f (t ': ts)
-> CoRec f (t ': ts)
foldRec1 f (x :& xs) = foldRec f (CoRec x) xs
firstField :: FoldRec ts ts
=> Rec (Maybe :. f) ts -> Maybe (CoRec f ts)
firstField RNil = Nothing
firstField v@(x :& _) = coRecTraverse getCompose $ foldRec aux (CoRec x) v
where aux :: CoRec (Maybe :. f) (t ': ts)
-> CoRec (Maybe :. f) (t ': ts)
-> CoRec (Maybe :. f) (t ': ts)
aux c@(CoRec (Compose (Just _))) _ = c
aux _ c = c
lastField :: FoldRec ts ts
=> Rec (Maybe :. f) ts -> Maybe (CoRec f ts)
lastField RNil = Nothing
lastField v@(x :& _) = coRecTraverse getCompose $ foldRec aux (CoRec x) v
where aux :: CoRec (Maybe :. f) (t ': ts)
-> CoRec (Maybe :. f) (t ': ts)
-> CoRec (Maybe :. f) (t ': ts)
aux _ c@(CoRec (Compose (Just _))) = c
aux c _ = c
onCoRec :: forall c f ts b g. (RPureConstrained c ts)
=> (forall a. (a ∈ ts, c a) => f a -> g b)
-> CoRec f ts -> g b
onCoRec f (CoRec x) = case getDict @c @ts x of
DictOnly -> f x
{-# INLINE onCoRec #-}
onField :: forall c ts b. (RPureConstrained c ts)
=> (forall a. (a ∈ ts, c a) => a -> b)
-> Field ts -> b
onField f x = getIdentity (onCoRec @c (fmap f) x)
{-# INLINE onField #-}
variantIndexOf :: forall f ts. CoRec f ts -> Int
variantIndexOf (CoRec x) = aux x
where aux :: forall a. NatToInt (RIndex a ts) => f a -> Int
aux _ = natToInt @(RIndex a ts)
{-# INLINE variantIndexOf #-}
asA :: NatToInt (RIndex t ts) => CoRec Identity ts -> Maybe t
asA = fmap getIdentity . asA'
{-# INLINE asA #-}
asA' :: forall t ts f. (NatToInt (RIndex t ts))
=> CoRec f ts -> Maybe (f t)
asA' f@(CoRec x)
| variantIndexOf f == natToInt @(RIndex t ts) = Just (unsafeCoerce x)
| otherwise = Nothing
{-# INLINE asA' #-}
match :: forall ts b. CoRec Identity ts -> Handlers ts b -> b
match (CoRec (Identity t)) hs = aux t
where aux :: forall a. RElem a ts (RIndex a ts) => a -> b
aux x = case rget @a hs of
H f -> f x
class RIndex t ts ~ i => Match1 t ts i where
match1' :: Handler r t -> Rec Maybe ts -> Either r (Rec Maybe (RDelete t ts))
instance Match1 t (t ': ts) 'Z where
match1' _ (Nothing :& xs) = Right xs
match1' (H h) (Just x :& _) = Left (h x)
instance (Match1 t ts i, RIndex t (s ': ts) ~ 'S i,
RDelete t (s ': ts) ~ (s ': RDelete t ts))
=> Match1 t (s ': ts) ('S i) where
match1' h (x :& xs) = (x :&) <$> match1' h xs
match1 :: (Match1 t ts (RIndex t ts),
RecApplicative ts,
RMap ts, RMap (RDelete t ts),
FoldRec (RDelete t ts) (RDelete t ts))
=> Handler r t
-> CoRec Identity ts
-> Either r (CoRec Identity (RDelete t ts))
match1 h = fmap (fromJust . firstField . rmap (Compose . fmap Identity))
. match1' h
. coRecToRec'
matchNil :: CoRec f '[] -> r
matchNil (CoRec x) = case x of _ -> error "matchNil: impossible"
newtype Handler b a = H (a -> b)
type Handlers ts b = Rec (Handler b) ts
restrictCoRec :: forall t ts f. (RecApplicative ts, FoldRec ts ts)
=> CoRec f (t ': ts) -> Either (f t) (CoRec f ts)
restrictCoRec r = maybe (Right (unsafeCoerce r)) Left (asA' @t r)
{-# INLINE restrictCoRec #-}
weakenCoRec :: (RecApplicative ts, FoldRec (t ': ts) (t ': ts))
=> CoRec f ts -> CoRec f (t ': ts)
weakenCoRec = fromJust . firstField . (Compose Nothing :&) . coRecToRec
restrictCoRecSafe :: forall t ts f. (RecApplicative ts, FoldRec ts ts)
=> CoRec f (t ': ts) -> Either (f t) (CoRec f ts)
restrictCoRecSafe = go . coRecToRec
where go :: Rec (Maybe :. f) (t ': ts) -> Either (f t) (CoRec f ts)
go (Compose Nothing :& xs) = Right (fromJust (firstField xs))
go (Compose (Just x) :& _) = Left x
asASafe :: (t ∈ ts, RecApplicative ts, RMap ts)
=> CoRec Identity ts -> Maybe t
asASafe c@(CoRec _) = rget $ coRecToRec' c
asA'Safe :: (t ∈ ts, RecApplicative ts, RMap ts)
=> CoRec f ts -> (Maybe :. f) t
asA'Safe c@(CoRec _) = rget $ coRecToRec c