{-# LANGUAGE CPP #-} {- | Description: Variants, i.e., labelled sums, generalizations of Either The HList library See for the public (safe) interface. The implementation here follows "Data.Dynamic", though Typeable is not needed. See @broken/VariantP.hs@ and @broken/VariantOld.hs@ for different approaches to open sums. -} module Data.HList.Variant where import Data.HList.FakePrelude import Data.HList.Record import Data.HList.HList import Data.HList.HListPrelude import Data.HList.HOccurs() import Data.HList.HArray import Text.ParserCombinators.ReadP hiding (optional) import Unsafe.Coerce import GHC.Exts (Constraint) #if __GLASGOW_HASKELL__ <= 906 import Data.Semigroup (Semigroup( .. )) #endif import Data.Data import Control.Applicative import LensDefs import Control.Monad -- * Labels for doctests {- $setup >>> import Data.HList.RecordPuns >>> let x = Label :: Label "x" >>> let y = Label :: Label "y" >>> let z = Label :: Label "z" >>> let _left = Label :: Label "left" >>> let _right = Label :: Label "right" >>> :set -XQuasiQuotes -XViewPatterns -XDataKinds -- * Creating Variants It is necessary to specify the order in which the fields occur, using a data type like >>> let p = Proxy :: Proxy '[Tagged "left" Char, Tagged "right" Int] Then this argument can be passed into 'mkVariant' >>> let v = mkVariant _left 'x' p >>> let w = mkVariant _right 5 p >>> :t v v :: Variant '[Tagged "left" Char, Tagged "right" Int] >>> :t w w :: Variant '[Tagged "left" Char, Tagged "right" Int] >>> [v,w] [V{left='x'},V{right=5}] -} -- ** Alternative: a 'Record' as the Proxy {- $mkVariant2 The type of mkVariant also allows using a 'Record' as the proxy. For example: >>> :{ let p2 = [pun| left right |] where left = 'a' right = (4::Int) :} >>> let v2 = mkVariant _left 'x' p2 >>> let w2 = mkVariant _right 5 p2 >>> :t v2 v2 :: Variant '[Tagged "left" Char, Tagged "right" Int] >>> :t w2 w2 :: Variant '[Tagged "left" Char, Tagged "right" Int] >>> (v2,w2) (V{left='x'},V{right=5}) -} -- ** A polymorphic Proxy {- $mkVariant3 It is also possible to leave the @Char@ and @Int@ as type variables, and have them inferred. >>> let p3 = Proxy :: Proxy '[Tagged "left" a, Tagged "right" b] Using @p3@ takes some care. The following attempt shows the problem: >>> :{ let v3' = mkVariant _left 'x' p3 w3' = mkVariant _right (5::Int) p3 :} >>> :t v3' v3' :: Variant '[Tagged "left" Char, Tagged "right" b] >>> :t w3' w3' :: Variant '[Tagged "left" a, Tagged "right" Int] Here each use of @p3@ does not constrain the type of the other use. In some cases those type variables will be inferred from other constraints, such as when putting the variants into a list >>> [v3', w3'] [V{left='x'},V{right=5}] In other cases the other tags will be defaulted to (), at least if `ExtendedDefaultRules` is enabled: >>> v3' V{left='x'} >>> :set -XNoExtendedDefaultRules >>> v3' ... ...No instance for (Show ...) arising from a use of ‘print’ ... Another way around this issue is to make sure that the proxy is bound in a monomorphic pattern. These are patterns that allow name shadowing. * @\p -> ...@ * @case e of p -> ...@ * @do p <- e; ...@ * implicit parameters @let ?p = e in ...@ * An example of the case: >>> :{ let (v3,w3) = case p3 of p -> (mkVariant _left 'x' p, mkVariant _right (5 :: Int) p) :} >>> :t v3 v3 :: Variant '[Tagged "left" Char, Tagged "right" Int] >>> :t w3 w3 :: Variant '[Tagged "left" Char, Tagged "right" Int] -} -- -------------------------------------------------------------------------- {- | @Variant vs@ has an implementation similar to 'Dynamic', except the contained value is one of the elements of the @vs@ list, rather than being one particular instance of 'Typeable'. >>> v .!. _right Nothing >>> v .!. _left Just 'x' In some cases the 'pun' quasiquote works with variants, >>> let f [pun| left right |] = (left,right) >>> f v (Just 'x',Nothing) >>> f w (Nothing,Just 5) >>> let add1 v = hMapV (Fun succ :: Fun '[Enum] '()) v >>> f (add1 v) (Just 'y',Nothing) >>> f (add1 w) (Nothing,Just 6) -} data Variant (vs :: [*]) = Variant !Int Any #if __GLASGOW_HASKELL__ > 707 -- the inferred role is phantom, which is not safe type role Variant representational #endif -- ** Unsafe operations -- | This is only safe if the n'th element of vs has type @Tagged t v@ unsafeMkVariant :: Int -- ^ n -> v -> Variant vs unsafeMkVariant n a = Variant n (unsafeCoerce a) {- | Safe when (e ~ e') given that > Tagged t e ~ HLookupByHNatR n v > Tagged t' e' ~ HLookupByHNatR n v' 'hUpdateAtLabel' is the safe version -} unsafeCastVariant :: Variant v -> Variant v' unsafeCastVariant (Variant n e) = Variant n e -- | in ghc>=7.8, 'Data.Coerce.coerce' is probably a better choice castVariant :: (RecordValuesR v ~ RecordValuesR v', SameLength v v') => Variant v -> Variant v' castVariant = unsafeCastVariant instance Relabeled Variant where relabeled = iso castVariant castVariant -- | private destructor. This is safe only if the value -- contained actually has type `e` unsafeUnVariant :: Variant v -> e unsafeUnVariant (Variant _ e) = unsafeCoerce e {- | This function is unsafe because it can lead to a runtime error when used together with the 'HExtend' instance (.*.) >>> print $ (Label :: Label "x") .=. (Nothing :: Maybe ()) .*. unsafeEmptyVariant V{*** Exception: invalid variant use 'mkVariant1' instead -} unsafeEmptyVariant :: Variant '[] unsafeEmptyVariant = unsafeMkVariant 0 () -- -------------------------------------------------------------------------- -- * Public constructor class HasField x (Variant vs) (Maybe v) => MkVariant x v vs | x vs -> v where mkVariant :: Label x -- ^ the tag -> v -- ^ value to be stored -> proxy vs -- ^ a helper to fix the ordering and types of the -- potential values that this variant contains. -- Typically this will be a 'Proxy', 'Record' or -- another 'Variant' -> Variant vs mkVariant1 l v = l .=. Just v .*. unsafeEmptyVariant instance (HFindLabel x vs n, HNat2Integral n, HasField x (Variant vs) (Maybe v)) => MkVariant x v vs where mkVariant _x y _p = unsafeMkVariant (hNat2Integral (Proxy :: Proxy n)) y -- done as a one-instance class instead of a function to be able to hide -- the 'n' type variable -- -------------------------------------------------------------------------- -- * Public destructor {- $note 'hLookupByLabel' (synonym '.!.') > (.!.) :: Variant v -> Label x -> Maybe e > hLookupByLabel :: Label x -> Variant v -> Maybe e 'hPrism' and 'hLens'' combine this with 'mkVariant' -} instance (HasField x (Record vs) a, HFindLabel x vs n, HNat2Integral n) => HasField x (Variant vs) (Maybe a) where hLookupByLabel _x (Variant n d) | hNat2Integral (Proxy :: Proxy n) == n = Just (unsafeCoerce d) | otherwise = Nothing splitVariant1 :: Variant (Tagged s x ': xs) -> Either x (Variant xs) splitVariant1 (Variant 0 x) = Left (unsafeCoerce x) splitVariant1 (Variant n x) = Right (Variant (n-1) x) -- | x ~ Tagged s t splitVariant1' :: Variant (x ': xs) -> Either x (Variant xs) splitVariant1' (Variant 0 x) = Left (unsafeCoerce x) splitVariant1' (Variant n x) = Right (Variant (n-1) x) extendVariant :: Variant l -> Variant (e ': l) extendVariant (Variant m e) = Variant (m+1) e -- -------------------------------------------------------------------------- -- * Prism {- | Make a @Prism (Variant s) (Variant t) a b@ out of a Label. See "Data.HList.Labelable".'hLens'' is a more overloaded version. Few type annotations are necessary because of the restriction that `s` and `t` have the same labels in the same order, and to get \"t\" the \"a\" in \"s\" is replaced with \"b\". -} class (SameLength s t, SameLabels s t) => HPrism x s t a b | x s -> a, x t -> b, -- lookup x s b -> t, x t a -> s -- update where hPrism :: (Choice p, Applicative f) => Label x -> p a (f b) -> p (Variant s) (f (Variant t)) instance ( MkVariant x b t, HasField x (Variant s) (Maybe a), -- labels in the HList are not changed at all: -- number, ordering, actual values are all constant SameLength s t, SameLabels s t, -- only the target of the prism can have it's type changed H2ProjectByLabels '[Label x] s si so, H2ProjectByLabels '[Label x] t ti to, so ~ to, -- to convince GHC the fundeps are satisfied HUpdateAtLabel Variant x b s t, HUpdateAtLabel Variant x a t s ) => HPrism x s t a b where hPrism x = prism (\b -> mkVariant x b Proxy) (\s -> case hLookupByLabel x s of Just a -> Right a Nothing -> Left (unsafeCastVariant s :: Variant t)) -- -------------------------------------------------------------------------- -- * Read -- | Variants are not opaque instance (ShowVariant vs) => Show (Variant vs) where showsPrec _ v = ("V{"++) . showVariant v . ('}':) -- | helper class for defining the Show instance class ShowVariant vs where showVariant :: Variant vs -> ShowS instance (ShowLabel l, Show v, ShowVariant (w ': ws)) => ShowVariant (Tagged l v ': w ': ws) where showVariant vs = case splitVariant1 vs of Left v -> \rest -> showLabel l ++ "=" ++ show v ++ rest Right wws -> showVariant wws where l = Label :: Label l instance (ShowLabel l, Show v, lv ~ Tagged l v) => ShowVariant '[lv] where showVariant vs = case splitVariant1 vs of Left v -> \rest -> showLabel l ++ "=" ++ show v ++ rest Right _ -> error "invalid variant" where l = Label :: Label l -- -------------------------------------------------------------------------- -- * Show -- | A corresponding read instance instance ReadVariant v => Read (Variant v) where readsPrec _ = readP_to_S $ do _ <- string "V{" r <- readVariant _ <- string "}" return r class ReadVariant vs where readVariant :: ReadP (Variant vs) instance ReadVariant '[] where readVariant = return unsafeEmptyVariant instance (ShowLabel l, Read v, ReadVariant vs, HOccursNot (Label l) (LabelsOf vs)) => ReadVariant (Tagged l v ': vs) where readVariant = do mlv <- optional lv case mlv of Nothing -> do rest <- readVariant return (l .=. mlv .*. rest) Just e -> do return (mkVariant l e p) where lv = do _ <- string (showLabel l) _ <- string "=" readS_to_P reads l = Label :: Label l p = Proxy :: Proxy (Tagged l v ': vs) -- * Data instance (Typeable (Variant v), GfoldlVariant v v, GunfoldVariant v v, VariantConstrs v) => Data (Variant v) where gfoldl = gfoldlVariant gunfold k z c = gunfoldVariant (\con -> k (z con)) (Proxy :: Proxy v) (constrIndex c - 1) toConstr v@(Variant n _) = case drop n (variantConstrs (dataTypeOf v) v) of c : _ -> c _ -> error "Data.HList.Variant.toConstr impossible" dataTypeOf x = let self = mkDataType (show (typeOf x)) (variantConstrs self x) in self class VariantConstrs (xs :: [*]) where variantConstrs :: DataType -> proxy xs -> [Constr] instance VariantConstrs '[] where variantConstrs _ _ = [] instance (ShowLabel l, VariantConstrs xs) => VariantConstrs (Tagged l e ': xs) where variantConstrs dt _ = mkConstr dt (showLabel (Label :: Label l)) [] Prefix : variantConstrs dt (Proxy :: Proxy xs) {- | [@implementation of gunfold for Variant@] In ghci > :set -ddump-deriv -XDeriveDataTypeable > data X a b c = A a | B b | C c deriving (Data,Typeable) shows that gunfold is defined something like > gunfold k z c = case constrIndex c of > 1 -> k (z Ghci1.A) > 2 -> k (z Ghci1.B) > _ -> k (z Ghci1.C) If we instead had > type X a b c = Variant [Tagged "A" a, Tagged "B" b, Tagged "C" c] Then we could write: > gunfold1 :: (forall b r. Data b => (b -> r) -> c r) > -> Variant [Tagged "A" a, Tagged "B" b, Tagged "C" c] > gunfold1 f c = case constrIndex c of > 1 -> f mkA > 2 -> f mkB > _ -> f mkC > where mkA a = mkVariant (Label :: Label "A") (a :: a) v > mkB b = mkVariant (Label :: Label "B") (b :: b) v > mkC c = mkVariant (Label :: Label "C") (c :: c) v > v = Proxy :: Proxy [Tagged "A" a, Tagged "B" b, Tagged "C" c] where @f = k.z@ -} class GunfoldVariant (es :: [*]) v where gunfoldVariant :: (forall b. Data b => (b -> Variant v) -> c (Variant v)) -- ^ @f = k . z@ -> Proxy es -> Int -> c (Variant v) instance (MkVariant l e v, Data e) => GunfoldVariant '[Tagged l e] v where gunfoldVariant f _ _ = f (\e -> mkVariant (Label :: Label l) (e :: e) Proxy) instance (MkVariant l e v, Data e, GunfoldVariant (b ': bs) v) => GunfoldVariant (Tagged l e ': b ': bs) v where gunfoldVariant f _ 0 = f (\e -> mkVariant (Label :: Label l) (e :: e) Proxy) gunfoldVariant f _ n = gunfoldVariant f (Proxy :: Proxy (b ': bs)) (n-1) class GfoldlVariant xs xs' where -- | the same as 'gfoldl', except the variant that is returned can have more -- possible values (needed to actually implement gfoldl). gfoldlVariant :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Variant xs -> c (Variant xs') instance (a ~ Tagged l v, MkVariant l v r, Data v, GfoldlVariant (b ': c) r) => GfoldlVariant (a ': b ': c) r where gfoldlVariant k z xxs = case splitVariant1 xxs of Right xs -> gfoldlVariant k z xs -- If the c@type variable in 'gfoldl' had a Functor constraint, -- this case could be extendVariant `fmap` gfoldl k z xs, -- and then 'GfoldlVariant' would be unnecessary Left x -> let mkV e = mkVariant (Label :: Label l) e Proxy in z mkV `k` x instance (Unvariant '[a] v, a ~ Tagged l v, Data v, MkVariant l v b) => GfoldlVariant '[a] b where gfoldlVariant k z xxs = z mkV `k` unvariant xxs where mkV e = mkVariant (Label :: Label l) e Proxy -- -------------------------------------------------------------------------- -- * Map -- | Apply a function to all possible elements of the variant newtype HMapV f = HMapV f -- | shortcut for @applyAB . HMapV@. 'hMap' is more general hMapV f v = applyAB (HMapV f) v -- | @hMapOutV f = unvariant . hMapV f@, except an ambiguous type -- variable is resolved by 'HMapOutV_gety' hMapOutV :: forall x y z f. (SameLength x y, HMapAux Variant (HFmap f) x y, Unvariant y z, HMapOutV_gety x z ~ y ) => f -> Variant x -> z hMapOutV f v = unvariant (hMapV f v :: Variant y) -- | resolves an ambiguous type in 'hMapOutV' type family HMapOutV_gety (x :: [*]) (z :: *) :: [*] type instance HMapOutV_gety (Tagged s x ': xs) z = Tagged s z ': HMapOutV_gety xs z type instance HMapOutV_gety '[] z = '[] -- | apply a function to all values that could be in the variant. instance (vx ~ Variant x, vy ~ Variant y, HMapAux Variant (HFmap f) x y, SameLength x y) => ApplyAB (HMapV f) vx vy where applyAB (HMapV f) x = hMapAux (HFmap f) x instance (ApplyAB f te te') => HMapAux Variant f '[te] '[te'] where hMapAux f v = case splitVariant1' v of Left te -> unsafeMkVariant 0 (applyAB f te :: te') Right _ -> error "HMapVAux: variant invariant broken" instance (ApplyAB f te te', HMapCxt Variant f (l ': ls) (l' ': ls')) => HMapAux Variant f (te ': l ': ls) (te' ': l' ': ls') where hMapAux f v = case splitVariant1' v of Left te -> unsafeMkVariant 0 (applyAB f te :: te') Right es -> extendVariant (hMapAux f es) -- -------------------------------------------------------------------------- -- * HUpdateAtLabel instance {- | > hUpdateAtLabel x e' (mkVariant x e proxy) == mkVariant x e' proxy > hUpdateAtLabel y e' (mkVariant x e proxy) == mkVariant x e proxy -} instance (HUpdateVariantAtLabelCxt l e v v' n _e) => HUpdateAtLabel Variant l e v v' where hUpdateAtLabel l e v = case hLookupByLabel l v of Just _e -> mkVariant l e (Proxy :: Proxy v') Nothing -> unsafeCastVariant v type HUpdateVariantAtLabelCxt l e v v' n _e = (HFindLabel l v n, HFindLabel l v' n, HUpdateAtHNatR n (Tagged l e) v ~ v', HasField l (Variant v) (Maybe _e), HasField l (Record v') e, MkVariant l e v') -- -------------------------------------------------------------------------- -- * HExtend instance {- | Extension for Variants prefers the first value > (l .=. Nothing) .*. v = v > (l .=. Just e) .*. _ = mkVariant l e Proxy -} instance (le ~ Tagged l (Maybe e), HOccursNot (Label l) (LabelsOf v)) => HExtend le (Variant v) where type HExtendR le (Variant v) = Variant (UnMaybe le ': v) Tagged (Just e) .*. _ = unsafeMkVariant 0 e Tagged Nothing .*. (Variant n e) = Variant (n+1) e type family UnMaybe le type instance UnMaybe (Tagged l (Maybe e)) = Tagged l e -- | used for 'HExtend' 'TIP' type instance UnMaybe (Maybe e) = e -- -------------------------------------------------------------------------- -- * Conversion to an untagged value class HAllEqVal (x :: [*]) (b :: Bool) | x -> b instance HAllEqVal '[] True instance HAllEqVal '[x] True instance (HEq a a' b, HAllEqVal (Tagged t a' ': xs) b2, HAnd b b2 ~ b3) => HAllEqVal (Tagged s a ': Tagged t a' ': xs) b3 class HAllEqVal' (x :: [*]) instance HAllEqVal' '[] instance HAllEqVal' '[x] instance (HAllEqVal' (ta ': xs), a' ~ a, ta ~ Tagged t a, ta' ~ Tagged t' a') => HAllEqVal' (ta' ': ta ': xs) {- | Similar to 'unvariant', except type variables in @v@ will be made equal to @e@ if possible. That allows the type of @Nothing@ to be inferred as @Maybe Char@. >>> unvariant' $ x .=. Nothing .*. mkVariant1 y 'y' 'y' However, this difference leads to more local error messages (@Couldn't match type ‘()’ with ‘Char’@), rather than the following with @unvariant@: > Fail > '("Variant", > '[Tagged "left" Char, Tagged "right" ()], > "must have all values equal to ", > e)) -} class Unvariant' v e | v -> e where unvariant' :: Variant v -> e instance (HAllEqVal' (Tagged () e ': v), Unvariant v e) => Unvariant' v e where unvariant' = unvariant {- | Convert a Variant which has all possibilities having the same type into a value of that type. Analogous to @either id id@. See also 'unvariant'' -} class Unvariant v e | v -> e where unvariant :: Variant v -> e instance (Unvariant1 b v e, HAllEqVal v b, HAllEqVal (Tagged () e ': v) b) => Unvariant v e where unvariant = unvariant1 (Proxy :: Proxy b) class Unvariant1 b v e | b v -> e where unvariant1 :: Proxy b -> Variant v -> e instance (v ~ Tagged t1 e) => Unvariant1 True (v ': vs) e where unvariant1 _ = unsafeUnVariant data UnvariantTypeMismatch (vs :: [*]) instance Fail (UnvariantTypeMismatch (v ': vs)) => Unvariant1 False (v ': vs) (UnvariantTypeMismatch (v ': vs)) where unvariant1 _ = error "Data.HList.Variant.Unvariant1 Fail must have no instances" instance Fail "Unvariant applied to empty variant" => Unvariant1 b '[] (Proxy "Unvariant applied to empty variant") where unvariant1 _ = error "Data.HList.Variant.Unvariant1 Fail must have no instances" {- | @Lens (Variant s) (Variant t) a b@ Analogue of @Control.Lens.chosen :: Lens (Either a a) (Either b b) a b@ -} unvarianted :: (Unvariant' s a, Unvariant' t b, SameLabels s t, -- extra constraints to reduce ambiguity SameLength s t, Functor f) => (a -> f b) -> Variant s -> f (Variant t) unvarianted f v@(Variant n _) = fmap (\e' -> unsafeMkVariant n e') (f (unvariant' v)) -- | @Lens' (Variant s) a@ -- -- where we might have @s ~ '[Tagged t1 a, Tagged t2 a]@ unvarianted' x = simple (unvarianted x) -- * Zip {- | Applies to variants that have the same labels in the same order. A generalization of > zipEither :: Either a b -> Either a b -> Maybe (Either (a,a) (b,b)) > zipEither (Left a) (Left a') = Just (Left (a,a')) > zipEither (Right a) (Right a') = Just (Right (a,a')) > zipEither _ _ = Nothing see 'HZip' for zipping other collections -} class ZipVariant x y xy | x y -> xy, xy -> x y where zipVariant :: Variant x -> Variant y -> Maybe (Variant xy) instance ZipVariant '[] '[] '[] where zipVariant _ _ = Nothing instance (tx ~ Tagged t x, ty ~ Tagged t y, txy ~ Tagged t (x,y), ZipVariant xs ys zs, MkVariant t (x,y) (txy ': zs)) => ZipVariant (tx ': xs) (ty ': ys) (txy ': zs) where zipVariant x y = case (splitVariant1 x, splitVariant1 y) of (Left x', Left y') -> Just (mkVariant (Label :: Label t) (x',y') Proxy) (Right x', Right y') -> extendVariant <$> zipVariant x' y' _ -> Nothing instance (HUnzip Variant (x2 ': xs) (y2 ': ys) (xy2 ': xys), SameLength xs ys, SameLength ys xys, tx ~ Tagged t x, ty ~ Tagged t y, txy ~ Tagged t (x,y)) => HUnzip Variant (tx ': x2 ': xs) (ty ': y2 ': ys) (txy ': xy2 ': xys) where hUnzip xy = case splitVariant1 xy of Left (x,y) -> (mkVariant (Label :: Label t) x Proxy, mkVariant (Label :: Label t) y Proxy) Right xy' | (x,y) <- hUnzip xy' -> (extendVariant x, extendVariant y) instance (Unvariant '[txy] txy, tx ~ Tagged t x, ty ~ Tagged t y, txy ~ Tagged t (x,y)) => HUnzip Variant '[tx] '[ty] '[txy] where hUnzip xy | Tagged (x,y) <- unvariant xy = (mkVariant1 Label x, mkVariant1 Label y) -- ** with a record {- | Apply a record of functions to a variant of values. The functions are selected based on those having the same label as the value. -} class (SameLength v v', SameLabels v v') => ZipVR fs v v' | fs v -> v' where -- | 'zipVR' is probably a better choice in most -- situations, since it requires that @fs@ has one function for every -- element of @v@ zipVR_ :: Record fs -> Variant v -> Variant v' instance (lv ~ Tagged l v, lv' ~ Tagged l v', HMemberM (Label l) (LabelsOf fs) b, HasFieldM l (Record fs) f, DemoteMaybe (v -> v) f ~ (v -> v'), MkVariant l v' (lv' ': rs), ZipVR fs vs rs) => ZipVR fs (lv ': vs) (lv' ': rs) where zipVR_ r lvs = case splitVariant1 lvs of Left v | v' <- hLookupByLabelM l r (id :: v -> v) v -> mkVariant l v' Proxy Right vs -> extendVariant $ zipVR_ r vs where l = Label :: Label l instance ZipVR fs '[] '[] where zipVR_ _ x = x {- | >>> let xy = x .*. y .*. emptyProxy >>> let p = Proxy `asLabelsOf` xy >>> let vs = [ mkVariant x 1.0 p, mkVariant y () p ] >>> zipVR (hBuild (+1) id) `map` vs [V{x=2.0},V{y=()}] -} zipVR :: (SameLabels fs v, SameLength fs v, ZipVR fs v v', ZipVRCxt fs v v') => Record fs -> Variant v -> Variant v' zipVR = zipVR_ {- | Lets 'zipVR' act as if @'ZipVR' fs v v'@ had an FD @v v' -> fs@ > ZipVRCxt [Tagged s f, Tagged t g] > [Tagged s fx, Tagged t gx] > [Tagged s fy, Tagged t gy] > = (f ~ (fx -> fy), g ~ (gx -> gy)) -} type family ZipVRCxt (fs :: [*]) (xs :: [*]) (ys :: [*]) :: Constraint type instance ZipVRCxt (Tagged s f ': fs) (Tagged s x ': xs) (Tagged s y ': ys) = (f ~ (x -> y), ZipVRCxt fs xs ys) type instance ZipVRCxt '[] '[] '[] = () -- * Eq instance Eq (Variant '[]) where _ == _ = True instance (Eq (Variant xs), Eq x) => Eq (Variant (x ': xs)) where v == v' = case (splitVariant1' v, splitVariant1' v') of (Left l, Left r) -> l == r (Right l, Right r) -> l == r _ -> False -- ** Alternative Eq -- | implemented like @and (zipWith (==) xs ys)@. Behaves the same as the Eq instances for 'Variant' eqVariant v v' = maybe False (hMapOutV UncurryEq) $ zipVariant v v' data UncurryEq = UncurryEq instance (ee ~ (e,e), Eq e, bool ~ Bool) => ApplyAB UncurryEq ee bool where applyAB _ (e,e') = e == e' -- * Ord instance Ord (Variant '[]) where compare _ _ = EQ instance (Ord x, Ord (Variant xs)) => Ord (Variant (x ': xs)) where compare a b = compare (splitVariant1' a) (splitVariant1' b) -- * Bounded instance (Bounded x, Bounded z, HRevAppR (Tagged s x ': xs) '[] ~ (Tagged t z ': sx), MkVariant t z (Tagged s x ': xs)) => Bounded (Variant (Tagged s x ': xs)) where minBound = mkVariant (Label :: Label s) (minBound :: x) Proxy maxBound = mkVariant (Label :: Label t) (maxBound :: z) Proxy -- * Enum {- | >>> let t = minBound :: Variant '[Tagged "x" Bool, Tagged "y" Bool] >>> [t .. maxBound] [V{x=False},V{x=True},V{y=False},V{y=True}] [@Odd behavior@] There are some arguments that this instance should not exist. The last type in the Variant does not need to be Bounded. This means that 'enumFrom' behaves a bit unexpectedly: >>> [False .. ] [False,True] >>> [t .. ] [V{x=False},V{x=True},V{y=False},V{y=True},V{y=*** Exception: Prelude.Enum.Bool.toEnum: bad argument This is a \"feature\" because it allows an @Enum (Variant '[Tagged \"a\" Bool, Tagged \"n\" 'Integer'])@ Another difficult choice is that the lower bound is @fromEnum 0@ rather than @minBound@: >>> take 5 [ minBound :: Variant '[Tagged "b" Bool, Tagged "i" Int] .. ] [V{b=False},V{b=True},V{i=0},V{i=1},V{i=2}] -} instance (Enum x, Bounded x, Enum (Variant (y ': z))) => Enum (Variant (Tagged s x ': y ': z)) where fromEnum v = case splitVariant1 v of Left x -> fromEnum x Right yz -> 1 + fromEnum (maxBound :: Tagged s x) + fromEnum yz toEnum n | m >= n = mkVariant (Label :: Label s) (toEnum n) Proxy | otherwise = extendVariant $ toEnum (n - m - 1) where m = fromEnum (maxBound :: Tagged s x) {- | While the instances could be written Enum (Variant '[]) Eq/Ord which cannot produce values, so they have instances for empty variants ('unsafeEmptyVariant'). Enum can produce values, so it is better that @fromEnum 0 :: Variant '[]@ fails with No instance for @Enum (Variant '[])@ than producing an invalid variant. -} instance Enum x => Enum (Variant '[Tagged s x]) where fromEnum v = case splitVariant1 v of Left x -> fromEnum x _ -> error "Data.HList.Variant fromEnum impossible" toEnum n = mkVariant (Label :: Label s) (toEnum n) Proxy -- * Ix (TODO) -- * Semigroup instance (Unvariant '[Tagged t x] x, Semigroup x) => Semigroup (Variant '[Tagged t x]) where a <> b = case (unvariant a, unvariant b) of (l, r) -> mkVariant (Label :: Label t) (l <> r) Proxy instance (Semigroup x, Semigroup (Variant (a ': b))) => Semigroup (Variant (Tagged t x ': a ': b)) where a <> b = case (splitVariant1 a, splitVariant1 b) of (Left l, Left r) -> mkVariant (Label :: Label t) (l <> r) Proxy (Left l, _) -> mkVariant (Label :: Label t) l Proxy (_, Left r) -> mkVariant (Label :: Label t) r Proxy (Right l, Right r) -> extendVariant $ l <> r -- * Monoid instance (Unvariant '[Tagged t x] x, Monoid x) => Monoid (Variant '[Tagged t x]) where mempty = mkVariant (Label :: Label t) mempty Proxy #if __GLASGOW_HASKELL__ <= 906 mappend a b = case (unvariant a, unvariant b) of (l, r) -> mkVariant (Label :: Label t) (mappend l r) Proxy #endif instance (Monoid x, Monoid (Variant (a ': b))) => Monoid (Variant (Tagged t x ': a ': b)) where mempty = extendVariant mempty #if __GLASGOW_HASKELL__ <= 906 mappend a b = case (splitVariant1 a, splitVariant1 b) of (Left l, Left r) -> mkVariant (Label :: Label t) (mappend l r) Proxy (Left l, _) -> mkVariant (Label :: Label t) l Proxy (_, Left r) -> mkVariant (Label :: Label t) r Proxy (Right l, Right r) -> extendVariant $ mappend l r #endif -- * Projection {- | convert a variant with more fields into one with fewer (or the same) fields. >>> let ty = Proxy :: Proxy [Tagged "left" Int, Tagged "right" Int] >>> let l = mkVariant _left 1 ty >>> let r = mkVariant _right 2 ty >>> map projectVariant [l, r] :: [Maybe (Variant '[Tagged "left" Int])] [Just V{left=1},Nothing] @'rearrangeVariant' = 'fromJust' . 'projectVariant'@ is one implementation of 'rearrangeVariant', since the result can have the same fields with a different order: >>> let yt = Proxy :: Proxy [Tagged "right" Int, Tagged "left" Int] >>> map projectVariant [l, r] `asTypeOf` [Just (mkVariant _left 0 yt)] [Just V{left=1},Just V{right=2}] -} class ProjectVariant x y where projectVariant :: Variant x -> Maybe (Variant y) instance (ProjectVariant x ys, HasField t (Variant x) (Maybe y), HOccursNot (Label t) (LabelsOf ys), ty ~ Tagged t y) => ProjectVariant x (ty ': ys) where projectVariant x = y `mplus` ys where t = Label :: Label t y = (\v -> mkVariant t v Proxy) <$> x .!. t ys = (mty .*.) <$> (projectVariant x :: Maybe (Variant ys)) mty = Tagged Nothing :: Tagged t (Maybe y) instance ProjectVariant x '[] where projectVariant _ = Nothing {- | @projectExtendVariant = fmap 'extendVariant' . 'projectVariant'@ where intermediate variant is as large as possible. Used to implement "Data.HList.Labelable".'projected' Note that: >>> let r = projectExtendVariant (mkVariant1 Label 1 :: Variant '[Tagged "x" Int]) >>> r :: Maybe (Variant '[Tagged "x" Integer]) Nothing -} class HAllTaggedLV y => ProjectExtendVariant x y where projectExtendVariant :: Variant x -> Maybe (Variant y) instance HAllTaggedLV y => ProjectExtendVariant '[] y where projectExtendVariant _ = Nothing instance (lv ~ Tagged l v, HMemberM lv y inY, ProjectExtendVariant' inY lv y, ProjectExtendVariant xs y ) => ProjectExtendVariant (lv ': xs) y where projectExtendVariant v = case splitVariant1' v of Left lv -> projectExtendVariant' (Proxy :: Proxy inY) lv Right v' -> projectExtendVariant v' class ProjectExtendVariant' (inY :: Maybe [*]) lv (y :: [*]) where projectExtendVariant' :: Proxy inY -> lv -> Maybe (Variant y) instance ProjectExtendVariant' Nothing lv y where projectExtendVariant' _ _ = Nothing instance (MkVariant l v y, lv ~ Tagged l v) => ProjectExtendVariant' (Just t) lv y where projectExtendVariant' _ (Tagged v) = Just (mkVariant (Label :: Label l) v Proxy) class (ProjectVariant x yin, ProjectVariant x yout) => SplitVariant x yin yout where splitVariant :: Variant x -> Either (Variant yin) (Variant yout) instance (-- implementation ProjectVariant x yin, ProjectVariant x yout, -- constraints to ensure exactly one of -- the uses of projectVariant gives a Just H2ProjectByLabels (LabelsOf yin) x xi xo, HRearrange (LabelsOf yin) xi yin, HRearrange (LabelsOf yout) xo yout, HLeftUnion xi xo xixo, HRearrange (LabelsOf x) xixo x, -- probably redundant HAllTaggedLV x, HAllTaggedLV yin, HAllTaggedLV yout) => SplitVariant x yin yout where splitVariant x = case (projectVariant x, projectVariant x) of (Nothing, Just yout) -> Right yout (Just yin, Nothing) -> Left yin _ -> error "Data.HList.Variant:splitVariant impossible" -- | @projectVariant . extendsVariant = Just@ (when the types match up) -- -- 'extendVariant' is a special case class (HAllTaggedLV y, HAllTaggedLV x) => ExtendsVariant x y where extendsVariant :: Variant x -> Variant y instance (MkVariant l e y, le ~ Tagged l e, ExtendsVariant (b ': bs) y) => ExtendsVariant (le ': b ': bs) y where extendsVariant v = case splitVariant1 v of Left e -> mkVariant (Label :: Label l) (e :: e) Proxy Right vs -> extendsVariant vs instance (HAllTaggedLV x, Unvariant '[le] e, MkVariant l e x, le ~ Tagged l e) => ExtendsVariant '[le] x where extendsVariant v = mkVariant (Label :: Label l) (unvariant v) Proxy -- | @rearrangeVariant@ is a specialization of 'extendsVariant' whose -- result is always . see also 'rearranged' rearrangeVariant :: (SameLength v v', ExtendsVariant v v') => Variant v -> Variant v' rearrangeVariant v = extendsVariant v instance (SameLength s a, ExtendsVariant s a, SameLength b t, ExtendsVariant b t) => Rearranged Variant s t a b where rearranged = iso rearrangeVariant rearrangeVariant -- | @Prism (Record tma) (Record tmb) (Variant ta) (Variant tb)@ -- -- see 'hMaybied'' hMaybied x = prism variantToHMaybied (\s -> case hMaybiedToVariants s of [a] -> Right a _ -> Left (hMapR HCastF s)) x data HCastF = HCastF instance (mx ~ Maybe x, my ~ Maybe y, HCast y x) => ApplyAB HCastF mx my where applyAB _ x = hCast =<< x {- | @Prism' (Record tma) (Variant ta)@ where @tma@ and @tmb@ are lists like > tma ~ '[Tagged x (Maybe a), Tagged y (Maybe b)] > ta ~ '[Tagged x a , Tagged y b ] If one element of the record is Just, the Variant will contain that element. Otherwise, the prism fails. [@Note@] The types work out to define a prism: @l = 'prism'' 'variantToHMaybied' ('listToMaybe' . 'hMaybiedToVariants')@ but the law: @s^?l ≡ Just a ==> l # a ≡ s@ is not followed, because we could have: @ s, s2 :: Record '[Tagged "x" (Maybe Int), Tagged "y" (Maybe Char)] s = hBuild (Just 1) (Just '2') s2 = hBuild (Just 1) Nothing v :: Variant '[Tagged "x" Int, Tagged "y" Char] v = mkVariant (Label :: Label "x") 1 Proxy @ So that @s^?l == Just v@. But @l#v == s2 /= s@, while the law requires @l#v == s@. hMaybied avoids this problem by only producing a value when there is only one present. -} hMaybied' x = simple (hMaybied (simple x)) class VariantToHMaybied v r | v -> r, r -> v where variantToHMaybied :: Variant v -> Record r instance VariantToHMaybied '[] '[] where variantToHMaybied _ = emptyRecord instance (VariantToHMaybied v r, HReplicateF nr ConstTaggedNothing () r, tx ~ Tagged t x, tmx ~ Tagged t (Maybe x)) => VariantToHMaybied (tx ': v) (tmx ': r) where variantToHMaybied v = case splitVariant1 v of Left x -> Record $ HCons (Tagged (Just x)) $ hReplicateF Proxy ConstTaggedNothing () Right rest -> case variantToHMaybied rest of Record a -> Record $ (Tagged Nothing :: Tagged t (Maybe x)) `HCons` a -- don't use (.*.) because we have (LabelsOf v ~ LabelsOf r), so -- the duplicate check (HRLabelSet) implied by (.*.) is redundant data ConstTaggedNothing = ConstTaggedNothing instance (y ~ Tagged t (Maybe e)) => ApplyAB ConstTaggedNothing x y where applyAB _ _ = Tagged Nothing -- | Every element of the record that is Just becomes one element -- in the resulting list. See 'hMaybied'' example types that @r@ -- and @v@ can take. hMaybiedToVariants :: (HFoldr HMaybiedToVariantFs [Variant '[]] r [Variant v], -- impl VariantToHMaybied v r -- evidence for typechecking ) => Record r -> [Variant v] hMaybiedToVariants (Record r) = hFoldr HMaybiedToVariantFs ([] :: [Variant '[]]) r data HMaybiedToVariantFs = HMaybiedToVariantFs instance (x ~ (Tagged t (Maybe e), [Variant v]), y ~ [Variant (Tagged t e ': v)], MkVariant t e (Tagged t e ': v)) => ApplyAB HMaybiedToVariantFs x y where applyAB _ (Tagged me, v) = case me of Just e -> mkVariant (Label :: Label t) e Proxy : map extendVariant v _ -> fmap extendVariant v