I made a variant of eqT
that would allow me work with the result like any other Bool
to write something like eqT' @a @T1 || eqT' @a @T2
. However, while that worked well in some cases, I found that I couldn't replace every use of eqT
with it. For example, I wanted to use it to write a variant of readMaybe
that would just be Just
when it was supposed to return a String
. While using eqT
allowed me to keep the type as String -> Maybe a
, using eqT'
requires the type to be String -> Maybe String
. Why is that? I know that I can do this via other means, but I want to know why this doesn't work. I suppose this has something to do with special treatment in case expressions for GADTs (a :~: b
being a GADT), but what is that special treatment?
Here is some example code of what I'm talking about:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
import Data.Typeable
import Text.Read
eqT' :: forall a b. (Typeable a, Typeable b) => Bool
eqT' = case eqT @a @b of
Just Refl -> True
_ -> False
readMaybeWithBadType1 :: forall a. (Typeable a, Read a) => String -> Maybe String
readMaybeWithBadType1 = if eqT' @a @String
then Just
else readMaybe
readMaybeWithBadType2 :: forall a. (Typeable a, Read a) => String -> Maybe String
readMaybeWithBadType2 = case eqT' @a @String of
True -> Just
False -> readMaybe
readMaybeWithGoodType :: forall a. (Typeable a, Read a) => String -> Maybe a
readMaybeWithGoodType = case eqT @a @String of
Just Refl -> Just
_ -> readMaybe
main :: IO ()
main = return ()
Changing the type of either readMaybeWithBadType
to return Maybe a
results in ghc complaining:
u.hs:16:14: error:
• Couldn't match type ‘a’ with ‘String’
‘a’ is a rigid type variable bound by
the type signature for:
readMaybeWithBadType1 :: forall a.
(Typeable a, Read a) =>
String -> Maybe a
at u.hs:14:5-80
Expected type: String -> Maybe a
Actual type: a -> Maybe a
• In the expression: Just
In the expression: if eqT' @a @String then Just else readMaybe
In an equation for ‘readMaybeWithBadType1’:
readMaybeWithBadType1 = if eqT' @a @String then Just else readMaybe
• Relevant bindings include
readMaybeWithBadType1 :: String -> Maybe a (bound at u.hs:15:5)
|
16 | then Just
| ^^^^
u.hs:21:17: error:
• Couldn't match type ‘a’ with ‘String’
‘a’ is a rigid type variable bound by
the type signature for:
readMaybeWithBadType2 :: forall a.
(Typeable a, Read a) =>
String -> Maybe a
at u.hs:19:5-80
Expected type: String -> Maybe a
Actual type: a -> Maybe a
• In the expression: Just
In a case alternative: True -> Just
In the expression:
case eqT' @a @String of
True -> Just
False -> readMaybe
• Relevant bindings include
readMaybeWithBadType2 :: String -> Maybe a (bound at u.hs:20:5)
|
21 | True -> Just
| ^^^^
I kind of get why it's complaining, but I don't see why it isn't a problem in readMaybeWithGoodType
.