{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} module Data.Vinyl.Optic.Plain.Class where import Data.Profunctor.Choice (Choice) import Data.Vinyl.Core import Data.Vinyl.Plus.Internal (prism') import Data.Vinyl.Plus.TypeLevel import Data.Vinyl.TypeLevel import Data.Vinyl.Types -- | This is a drop-in replacement for the 'RElem' class from @Data.Vinyl.Lens@. -- The functions it provides work on 'CoRec's instead of 'Rec's. -- It also provides a lifting function 'clift', which has no -- equivalent operation on 'Rec's. -- If 'CoRec' were merged into @vinyl@, this typeclass could be -- eliminated, and its methods could be added to 'RElem'. class i ~ RIndex r rs => RElem (r :: k) (rs :: [k]) (i :: Nat) where -- | We can get a prism for getting and setting values in a 'CoRec'. Morally, -- -- > cprism :: Prism' (CoRec f rs) (f r) cprism :: (Choice p, Applicative g) => proxy r -> p (f r) (g (f r)) -> p (CoRec f rs) (g (CoRec f rs)) -- | Get a value from a 'CoRec'. Note that, unlike 'rget', -- this function may not find the requested element, so -- the result is wrapped in 'Maybe'. cget :: proxy r -> CoRec f rs -> Maybe (f r) -- | If the element in the 'CoRec' is of a certain type, -- modify it. Otherwise, leave the 'CoRec' unchanged. cmodify :: (f r -> f r) -> CoRec f rs -> CoRec f rs -- | If the element in the 'CoRec' is of a certain type, -- replace it. This function is provided for symmetry -- with 'rput', but it is not typically useful. -- Usually, 'clift' is more useful. cput :: f r -> CoRec f rs -> CoRec f rs -- | Lift an element into a 'CoRec'. clift :: f r -> CoRec f rs instance (r ~ s) => RElem r (s ': rs) 'Z where cprism p = prism' clift (cget p) clift = CoRecHere cget _ (CoRecHere v) = Just v cget _ (CoRecThere _) = Nothing cput v (CoRecHere _) = CoRecHere v cput _ r@(CoRecThere _) = r cmodify f (CoRecHere v) = CoRecHere (f v) cmodify _ r@(CoRecThere _) = r instance (RIndex r (s ': rs) ~ 'S i, RElem r rs i) => RElem r (s ': rs) ('S i) where cprism p = prism' clift (cget p) clift v = CoRecThere (clift v) cget _ (CoRecHere _) = Nothing cget proxy (CoRecThere c) = cget proxy c cput _ r@(CoRecHere _) = r cput v (CoRecThere r) = CoRecThere (cput v r) cmodify _ r@(CoRecHere _) = r cmodify f (CoRecThere r) = CoRecThere (cmodify f r) class is ~ RImage sub super => RSubset (sub :: [k]) (super :: [k]) is where -- | Upcast a 'CoRec' to another 'CoRec' that could be -- inhabited by additional types. ccast :: CoRec f sub -> CoRec f super instance RSubset '[] super '[] where ccast _ = error "CSubset: an empty CoRec is not possible" instance (RElem r super i , RSubset sub super is) => RSubset (r ': sub) super (i ': is) where ccast (CoRecHere v) = clift v ccast (CoRecThere cr) = ccast cr -- | Two record types are equivalent when they are subtypes of each other. type REquivalent rs ss is js = (RSubset rs ss is, RSubset ss rs js) -- | A shorthand for 'RElem' which supplies its index. type RElem' r rs = RElem r rs (RIndex r rs) -- | An infix operator for 'RMember\''. type r ∈ rs = RElem' r rs -- | A shorthand for 'RSubset' which supplies its image. type RSubset' rs ss = RSubset rs ss (RImage rs ss) -- | An infix operator for 'RSubset\'' type rs ⊆ ss = RSubset' rs ss -- | A shorthand for 'REquivalent' which supplies its images. type REquivalent' rs ss = REquivalent rs ss (RImage rs ss) (RImage ss rs) -- | An infix operator for 'REquivalent\'' type rs ≅ ss = REquivalent' rs ss -- | A non-unicode equivalent of @(⊆)@. type rs <: ss = rs ⊆ ss -- | A non-unicode equivalent of @(≅)@. type rs :~: ss = rs ≅ ss -- type CElem r rs = CElemX r rs (RIndex r rs) -- type CSubset sub super = CSubsetX sub super (RImage sub super) -- example :: CoRec Maybe '[Bool,Int,Double] -- example = clift (Just (4 :: Int)) -- -- example2 :: CoRec Maybe '[Bool,Int] -> CoRec Maybe '[Int,Bool,Char] -- example2 = ccast -- -- example3 :: Maybe Int -> CoRec Maybe '[Bool,Int] -> CoRec Maybe '[Bool,Int] -- example3 = cput -- -- example4 :: CoRec Maybe '[Bool,Int] -> Maybe (Maybe Int) -- example4 = cget (Nothing :: Maybe Int)