@@ -18,7 +18,7 @@ import Data.Kind (Type)
18
18
import Data.Maybe (isJust , isNothing )
19
19
import Data.Monoid (Monoid (.. ))
20
20
import Data.Semigroup (Semigroup (.. ))
21
- import Data.Type.Equality (testEquality , (:~:) (.. ), (:~~:) (.. ))
21
+ import Data.Type.Equality (TestEquality ( .. ) , (:~:) (.. ), (:~~:) (.. ))
22
22
import GHC.Generics ((:*:) (.. ), (:+:) (.. ))
23
23
24
24
import qualified Type.Reflection as TR
@@ -28,9 +28,10 @@ import Data.Kind (Constraint)
28
28
#endif
29
29
30
30
-- $setup
31
- -- >>> :set -XKindSignatures -XGADTs -XTypeOperators
31
+ -- >>> :set -XKindSignatures -XGADTs -XTypeOperators -XStandaloneDeriving -XQuantifiedConstraints
32
32
-- >>> import Data.Type.Equality
33
33
-- >>> import Data.Functor.Sum
34
+ -- >>> import Data.Maybe (isJust, isNothing)
34
35
-- >>> import GHC.Generics
35
36
36
37
-- | 'Show'-like class for 1-type-parameter GADTs. @GShow t => ...@ is equivalent to something
@@ -195,6 +196,75 @@ instance (GRead a, GRead b) => GRead (a :+: b) where
195
196
-- | A class for type-contexts which contain enough information
196
197
-- to (at least in some cases) decide the equality of types
197
198
-- occurring within them.
199
+ --
200
+ -- This class is sometimes confused with 'TestEquality' from base.
201
+ -- 'TestEquality' only checks /type equality/.
202
+ --
203
+ -- Consider
204
+ --
205
+ -- >>> data Tag a where TagInt1 :: Tag Int; TagInt2 :: Tag Int
206
+ --
207
+ -- The correct @'TestEquality' Tag@ instance is
208
+ --
209
+ -- >>> :{
210
+ -- instance TestEquality Tag where
211
+ -- testEquality TagInt1 TagInt1 = Just Refl
212
+ -- testEquality TagInt1 TagInt2 = Just Refl
213
+ -- testEquality TagInt2 TagInt1 = Just Refl
214
+ -- testEquality TagInt2 TagInt2 = Just Refl
215
+ -- :}
216
+ --
217
+ -- While we can define
218
+ --
219
+ -- @
220
+ -- instance 'GEq' Tag where
221
+ -- 'geq' = 'testEquality'
222
+ -- @
223
+ --
224
+ -- this will mean we probably want to have
225
+ --
226
+ -- @
227
+ -- instance 'Eq' Tag where
228
+ -- _ '==' _ = True
229
+ -- @
230
+ --
231
+ -- /Note:/ In the future version of @some@ package (to be released around GHC-9.6 / 9.8) the
232
+ -- @forall a. Eq (f a)@ constraint will be added as a constraint to 'GEq',
233
+ -- with a law relating 'GEq' and 'Eq':
234
+ --
235
+ -- @
236
+ -- 'geq' x y = Just Refl ⇒ x == y = True ∀ (x :: f a) (y :: f b)
237
+ -- x == y ≡ isJust ('geq' x y) ∀ (x, y :: f a)
238
+ -- @
239
+ --
240
+ -- So, the more useful @'GEq' Tag@ instance would differentiate between
241
+ -- different constructors:
242
+ --
243
+ -- >>> :{
244
+ -- instance GEq Tag where
245
+ -- geq TagInt1 TagInt1 = Just Refl
246
+ -- geq TagInt1 TagInt2 = Nothing
247
+ -- geq TagInt2 TagInt1 = Nothing
248
+ -- geq TagInt2 TagInt2 = Just Refl
249
+ -- :}
250
+ --
251
+ -- which is consistent with a derived 'Eq' instance for 'Tag'
252
+ --
253
+ -- >>> deriving instance Eq (Tag a)
254
+ --
255
+ -- Note that even if @a ~ b@, the @'geq' (x :: f a) (y :: f b)@ may
256
+ -- be 'Nothing' (when value terms are inequal).
257
+ --
258
+ -- The consistency of 'GEq' and 'Eq' is easy to check by exhaustion:
259
+ --
260
+ -- >>> 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
261
+ -- >>> (checkFwdGEq TagInt1 TagInt1, checkFwdGEq TagInt1 TagInt2, checkFwdGEq TagInt2 TagInt1, checkFwdGEq TagInt2 TagInt2)
262
+ -- (True,True,True,True)
263
+ --
264
+ -- >>> 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)
265
+ -- >>> (checkBwdGEq TagInt1 TagInt1, checkBwdGEq TagInt1 TagInt2, checkBwdGEq TagInt2 TagInt1, checkBwdGEq TagInt2 TagInt2)
266
+ -- (True,True,True,True)
267
+ --
198
268
#if __GLASGOW_HASKELL__ >= 810
199
269
type GEq :: (k -> Type ) -> Constraint
200
270
#endif
0 commit comments