A class for type-contexts which contain enough information to (at least in some cases) decide the equality of types occurring within them.
This class is sometimes confused with TestEquality from base. TestEquality only checks type equality.
Consider
data Tag a where TagInt1 :: Tag Int; TagInt2 :: Tag IntThe correct TestEquality Tag instance is
:{instance TestEquality Tag where testEquality TagInt1 TagInt1 = Just Refl testEquality TagInt1 TagInt2 = Just Refl testEquality TagInt2 TagInt1 = Just Refl testEquality TagInt2 TagInt2 = Just Refl:}
While we can define
instance GEq Tag where
geq = testEquality
this will mean we probably want to have
instance Eq Tag where
_ == _ = True
Note: In the future version of some package (to be released around GHC-9.6 / 9.8) the
forall a. Eq (f a) constraint will be added as a constraint to GEq,
with a law relating GEq and Eq:
geq x y = Just Refl ⇒ x == y = True ∀ (x :: f a) (y :: f b)
x == y ≡ isJust (geq x y) ∀ (x, y :: f a)
So, the more useful GEq Tag instance would differentiate between
different constructors:
:{instance GEq Tag where geq TagInt1 TagInt1 = Just Refl geq TagInt1 TagInt2 = Nothing geq TagInt2 TagInt1 = Nothing geq TagInt2 TagInt2 = Just Refl:}
which is consistent with a derived Eq instance for Tag
deriving instance Eq (Tag a)Note that even if a ~ b, the geq (x :: f a) (y :: f b) may
be Nothing (when value terms are inequal).
The consistency of GEq and Eq is easy to check by exhaustion:
let checkFwdGEq :: (forall a. Eq (f a), GEq f) => f a -> f b -> Bool; checkFwdGEq x y = case geq x y of Just Refl -> x == y; Nothing -> True(checkFwdGEq TagInt1 TagInt1, checkFwdGEq TagInt1 TagInt2, checkFwdGEq TagInt2 TagInt1, checkFwdGEq TagInt2 TagInt2)(True,True,True,True)
let checkBwdGEq :: (Eq (f a), GEq f) => f a -> f a -> Bool; checkBwdGEq x y = if x == y then isJust (geq x y) else isNothing (geq x y)(checkBwdGEq TagInt1 TagInt1, checkBwdGEq TagInt1 TagInt2, checkBwdGEq TagInt2 TagInt1, checkBwdGEq TagInt2 TagInt2)(True,True,True,True)
Methods
geq :: f a -> f b -> Maybe (a :~: b)Produce a witness of type-equality, if one exists.
A handy idiom for using this would be to pattern-bind in the Maybe monad, eg.:
extract :: GEq tag => tag a -> DSum tag -> Maybe a extract t1 (t2 :=> x) = do Refl <- geq t1 t2 return xOr in a list comprehension:
extractMany :: GEq tag => tag a -> [DSum tag] -> [a] extractMany t1 things = [ x | (t2 :=> x) <- things, Refl <- maybeToList (geq t1 t2)](Making use of the
DSumtype from Data.Dependent.Sum in both examples)
Instances10GEq, …
GEq SCharDefined in some-1.0.6 · Data.GADT.InternalGEq SSymbolDefined in some-1.0.6 · Data.GADT.InternalGEq SNatDefined in some-1.0.6 · Data.GADT.InternalGEq TypeRepDefined in some-1.0.6 · Data.GADT.InternalGEq ((:~:) a)Defined in some-1.0.6 · Data.GADT.InternalGEq ((:~~:) a)Defined in some-1.0.6 · Data.GADT.Internal(GEq a, GEq b) => GEq (Product a b)Defined in some-1.0.6 · Data.GADT.Internal(GEq a, GEq b) => GEq (Sum a b)Defined in some-1.0.6 · Data.GADT.Internal(GEq a, GEq b) => GEq (a :*: b)Defined in some-1.0.6 · Data.GADT.Internal(GEq f, GEq g) => GEq (f :+: g)Defined in some-1.0.6 · Data.GADT.Internal