Skip to content

Commit 235f631

Browse files
authored
Merge pull request #53 from haskellari/more-geq-docs
Resolve #47. Add explanation between GEq and TestEquality difference.
2 parents 00e4232 + 7a86f32 commit 235f631

File tree

1 file changed

+72
-2
lines changed

1 file changed

+72
-2
lines changed

src/Data/GADT/Internal.hs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import Data.Kind (Type)
1818
import Data.Maybe (isJust, isNothing)
1919
import Data.Monoid (Monoid (..))
2020
import Data.Semigroup (Semigroup (..))
21-
import Data.Type.Equality (testEquality, (:~:) (..), (:~~:) (..))
21+
import Data.Type.Equality (TestEquality (..), (:~:) (..), (:~~:) (..))
2222
import GHC.Generics ((:*:) (..), (:+:) (..))
2323

2424
import qualified Type.Reflection as TR
@@ -28,9 +28,10 @@ import Data.Kind (Constraint)
2828
#endif
2929

3030
-- $setup
31-
-- >>> :set -XKindSignatures -XGADTs -XTypeOperators
31+
-- >>> :set -XKindSignatures -XGADTs -XTypeOperators -XStandaloneDeriving -XQuantifiedConstraints
3232
-- >>> import Data.Type.Equality
3333
-- >>> import Data.Functor.Sum
34+
-- >>> import Data.Maybe (isJust, isNothing)
3435
-- >>> import GHC.Generics
3536

3637
-- |'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
195196
-- |A class for type-contexts which contain enough information
196197
-- to (at least in some cases) decide the equality of types
197198
-- 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+
--
198268
#if __GLASGOW_HASKELL__ >= 810
199269
type GEq :: (k -> Type) -> Constraint
200270
#endif

0 commit comments

Comments
 (0)