For the following code:
module Main where
data EitherOr a b = Hello a | Goodbye b deriving Show
instance (Eq a, Eq b) => Eq (EitherOr a b) where
(==) (Hello x) (Hello x') = x == x'
(==) (Goodbye x) (Goodbye x') = x == x'
(==) _ _ = False
main :: IO ()
main = do
print (Hello 2 == Hello 2)
-- print (Hello 3 == Hello 2)
-- print (Goodbye 3 == Goodbye 3)
-- print (Goodbye 4 == Goodbye 3)
-- print (Hello 3 == Goodbye 3)
executed under runhaskell, i.e., under ghc, I get the following error:
• Ambiguous type variable ‘b0’ arising from a use of ‘==’
prevents the constraint ‘(Eq b0)’ from being solved.
Probable fix: use a type annotation to specify what ‘b0’ should be.
These potential instances exist:
instance Eq Ordering -- Defined in ‘GHC.Classes’
instance Eq Integer
-- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
instance (Eq a, Eq b) => Eq (EitherOr a b)
-- Defined at /tmp/runghcXXXX61964-0.hs:5:10. <-- This is because I am using org-mode source blocks
...plus 23 others
...plus 11 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the first argument of ‘print’, namely ‘(Hello 2 == Hello 2)’
In a stmt of a 'do' block: print (Hello 2 == Hello 2)
In the expression: do print (Hello 2 == Hello 2)
|
12 | print (Hello 2 == Hello 2)
| ^^^^^^^^^^^^^^^^^^
I thought I could give the compiler a type hint by doing
print ((Hello (2 :: Int)) == (Hello (2 :: Int)))
or something similar, but that doesn't seem to be enough. I see that a and b are polymorphic, but I thought the use of == in main might be enough to help the compiler infer types.
Next I loaded the data type and typeclass instance in ghci and explored the types a bit and found that, for instance
λ> :t Hello (2 :: Int)
Hello (2 :: Int) :: EitherOr Int b
As expected. Again in ghci, I do more exploration and see that default types are being used
λ> :t (Hello 2 == Hello 2)
<interactive>:1:2: warning: [-Wtype-defaults]
• Defaulting the following constraints to type ‘Integer’
(Eq a0) arising from a use of ‘==’ at <interactive>:1:2-19
(Num a0) arising from the literal ‘2’ at <interactive>:1:8
• In the expression: (Hello 2 == Hello 2)
<interactive>:1:2: warning: [-Wtype-defaults]
• Defaulting the following constraint to type ‘()’
Eq b0 arising from a use of ‘==’
• In the expression: (Hello 2 == Hello 2)
(Hello 2 == Hello 2) :: Bool
which is what I want, of course.
Then I actually execute the code in ghci and get the right answer with some defaulting going on
λ> Hello 2 == Hello 2
<interactive>:27:1: warning: [-Wtype-defaults]
• Defaulting the following constraints to type ‘Integer’
(Eq a0) arising from a use of ‘==’ at <interactive>:27:1-18
(Num a0) arising from the literal ‘2’ at <interactive>:27:7
• In the expression: Hello 2 == Hello 2
In an equation for ‘it’: it = Hello 2 == Hello 2
<interactive>:27:1: warning: [-Wtype-defaults]
• Defaulting the following constraint to type ‘()’
Eq b0 arising from a use of ‘==’
• In the expression: Hello 2 == Hello 2
In an equation for ‘it’: it = Hello 2 == Hello 2
<interactive>:27:1: warning: [-Wtype-defaults]
• Defaulting the following constraints to type ‘Integer’
(Eq a0) arising from a use of ‘==’ at <interactive>:27:1-18
(Num a0) arising from the literal ‘2’ at <interactive>:27:7
• In the expression: Hello 2 == Hello 2
In an equation for ‘it’: it = Hello 2 == Hello 2
<interactive>:27:1: warning: [-Wtype-defaults]
• Defaulting the following constraint to type ‘()’
Eq b0 arising from a use of ‘==’
• In the expression: Hello 2 == Hello 2
In an equation for ‘it’: it = Hello 2 == Hello 2
True
But the same code executed under runhaskell, i.e., under ghc compilation, fails with the error I first gave. What do I need to learn here?
Hello (2 :: Int)
choosesa
(good!) but does not allow GHC to inferb
(bad). GHCi uses more aggressive defaulting rules and choosesb = ()
(I think), but GHC is less eager to do that, since it might lead to unwanted surprising results, in the general case. Hence, we need an annotation for the whole expressionHello 2 :: EitherOr MyTypeA MyTypeB
(as done below by Thomas). – chi