I was trying to understand why Haskell's show treats
a list of chars different from a list of e.g. integers
even without the FlexibleInstances Pragma.
Having read through the documentation of Show, I realized
that I don't really understand how Haskell chooses methods
for instances of type classes.
Consider the following code:
class MyShow a where
myShow :: a -> String
myShowList :: [a] -> String
myShowTuple :: (a, b) -> String
myShowList xs = "Default List Implementation"
myShowTuple t = "Default Tuple Implementation"
instance MyShow Char where
myShow c = "One Char"
myShowList xs = "List of Chars"
myShowTuple t = "Char Tuple"
instance MyShow Int where
myShow n = "One Int"
myShowList xs = "List of Integers"
myShowTuple t = "Int Tuple"
instance MyShow Float where
myShow n = show n
instance (MyShow a) => MyShow [a] where
myShow = myShowList
instance (MyShow a) => MyShow (a, b) where
myShowTuple t = "foo"
myShow = myShowTuple
Now if I call e.g.
myShow (5::Int,5::Int)
I would expect that Haskell thinks
'Oh, myShow got a tuple as an argument. Let's see which
implementation I have to call.'
and chooses the last one which in return would result
in "foo".
Obviously, this is not the case.
Haskell seems to look at the content of the tuple (namely
the type of a) and decides to call the corresponding method,
resulting in "Int Tuple".
Why is this?
An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.
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.
The second line, deriving (Eq, Show) , is called the deriving clause; it specifies that we want the compiler to automatically generate instances of the Eq and Show classes for our Pair type. The Haskell Report defines a handful of classes for which instances can be automatically generated.
Type and data type refer to exactly the same concept. The Haskell keywords type and data are different, though: data allows you to introduce a new algebraic data type, while type just makes a type synonym. See the Haskell wiki for details.
When you write myShow (5::Int, 5::Int), Haskell does say "Oh, myShow got a tuple as an argument. Let's see which implementation I have to call." and it does choose the last one, i.e. myShow = myShowTuple. But that doesn't mean the result will be "foo". It means that the result of calling myShow (5::Int, 5::Int) will be the same as the result of calling myShowTuple (5 :: Int, 5 :: Int).
So now Haskell has to decide which version of myShowTuple it has to call. Since myShowTuple has type MyShow a => (a, b) -> String, the version of myShowTuple that's defined on the second-to-last line has type MyShow a => ((a, c), b) -> String, so that one doesn't fit. The one defined on line 17 has type (Int, b) -> String, so that one does fit. So that's the one that is picked.
Haskell's thought process is something like this:
MyShow for (5 :: Int, 5 :: Int) or (Int, Int)
MyShow a => MyShow (a, b)
a => a and b => a) it selects this instance.a, in this case Int, is also an instance of MyShow, which it is. Notice: This check happens after the instance is selected.myShow (5 :: Int, 5 :: Int) call's the tuple's myShow and becomes myShowTuple (5 :: Int, 5 :: Int)
myShowTuple since this has the type (a, b) and in the tuple's case, a is (a, b) so myShowTuple has the type ((a, b) ,c) which is clearly not a match.MyShow a => (a, b) -> String) and since a in this case has the type Int haskell resolves this to the Int instance of MyShow
myShowTuple
"Int Tuple" Just a sidenote, if this was an actual implementation of Show you'd want something like this for myShowTuple
myShowTuple :: MyShow b => (a, b) -> String
Otherwise you have no way to actual format that b which is after all, any type.
This would make
instance (MyShow a) => MyShow (a, b) where
...
into
instance (MyShow a, MyShow b) => MyShow (a, b) where
...
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