The type of (==)
is Eq a => a -> a -> Bool
but I can imagine a more general version Eq a b => a -> b -> Bool
that is false when the types don't match and is just the usual kind of equality when the types match. Why is this not the case?
The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.
If you need to figure out what the type of an object is in a Haskell program, I hope this is helpful. Note that if you are in GHCI, you can just put :type before your expression to determine the expression's type, or use :set +t to see the type of every expression in GHCI.
In Haskell, every statement is considered as a mathematical expression and the category of this expression is called as a Type. You can say that "Type" is the data type of the expression used at compile time. To learn more about the Type, we will use the ":t" command.
What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.
We can define that in terms of both Eq
and Typeable
from Data.Typeable
. We can compare the types of things that are Typeable
to check that they are the same type.
(?==) :: (Eq b, Typeable a, Typeable b) => a -> b -> Bool
x ?== y = cast x == Just y
cast
checks that a value of one Typeable
type is actually the same type as another. It returns Just
the input if they are the same type or Nothing
if the types are different.
Here are a few examples demonstrating the desired behavior.
> 7 ?== 7
True
> 7 ?== "hello"
False
> 7 ?== 5
False
> (7 :: Int) ?== (7 :: Integer)
False
(==) :: Eq a b => a -> b -> Bool
wouldn't be of as much use as you'd think.
To determine an instance of 2-parameter Eq
, you need to know both types (well enough to match an instance). Any callers of this ==
would need to either concretely know a
and b
, or pass the constraint on in their interface; then their own callers either need to know what a
and b
are, or pass the constraint on.... at some point the Eq
instance has to be selected, rather than provided via a constraint, and that involves knowing a
and b
at compile time.
At that point, why would you bother? If you know a
and b
are the same, then a single parameter Eq
is enough to compare them. And if you know they are different, then you know the answer is False
and you don't need any instance to tell you that. And if you don't know whether they are the same or different, then by definition you don't know enough to select an Eq
instance, so you can't call ==
(or an Eq
-constrained function) at all!
So this wouldn't help you get an a
and b
of unknown types (that implement equality) from separate sources and then compare them. They would have had to come together with an Eq a b
instance, which is effectively a compile-time proof of whether they are the same type or not,
The Typeable
version in @Cirdec's answer is very different, and much more useful. Here each type independentlly supports Typeable
and one of them supports single-parameter Eq
; you can get these two values from two different sources where you don't know if they are the same or not, but you know that you can check the type of each; the a
source has to know a
well enough to select a Typeable
instance, and the b
source has to know b
well enough to select Typeable
and Eq
instances, but neither source has to know anything about the other type, and no code has to know both types at once well enough to tell whether they are the same (at compile time). The 2-parameter Eq
forces both types to be known at once, somewhere, which would make it near-worthless.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With