How does Haskell choose methods for instances of type classes?

I was trying to understand why the Haskell show considers a list of characters other than a list, for example. whole even without FlexibleInstances Pragma.

After reading the show documentation, I realized that I really do not understand how Haskell selects 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, for example,

 myShow (5::Int,5::Int) 

I would expect Haskell to think, "Oh, myShow got a tuple as an argument. Let's see which ones I should call." and selects the latter, which in turn will result in "foo" . Obviously, this is not so. It seems that Haskell considers the contents of the tuple (namely type a ) and decides to call the appropriate method, which results in "Int Tuple" .

Why is this?

+6
source share
2 answers

When you write myShow (5::Int, 5::Int) , Haskell says: “Oh, myShow received the tuple as an argument. Let's see which implementation I need to call.” and he chooses the latter, i.e. myShow = myShowTuple . But this does not mean that the result will be "foo". This means that the result of a call to myShow (5::Int, 5::Int) will be the same as the result of a call to myShowTuple (5 :: Int, 5 :: Int) .

So, now Haskell has to decide which version of myShowTuple it should call. Since myShowTuple is of type MyShow a => (a, b) -> String , the version of myShowTuple defined in the second-last line is of type MyShow a => ((a, c), b) -> String , so it doesn't fits. The one defined on line 17 is of type (Int, b) -> String , so it works. So the one that is selected.

+9
source

Haskell's thought process looks something like this:

  • He is trying to figure out an instance of MyShow for (5 :: Int, 5 :: Int) or (Int, Int)
  • Then it finds an instance of MyShow a => MyShow (a, b)
  • Since this combines ( a => a and b => a ), he selects this instance.
  • It checks that a , in this case Int , is also an instance of MyShow , which it is. Note. This check occurs after . Instance selected.
  • myShow (5 :: Int, 5 :: Int) calls the MyShow set and becomes myShowTuple (5 :: Int, 5 :: Int)
    • It does not call myShowTuple , since it is of type (a, b) , and in the case of a tuple a is (a, b) , so myShowTuple has type ((a, b) ,c) , which clearly does not match.
  • This type has MyShow a => (a, b) -> String) , and since a in this case is of type Int haskell, it solves this with an instance of Int MyShow
  • It launches the corresponding myShowTuple
  • You get "Int Tuple"

Just sitting, if that was the actual implementation of the Show , you need something similar for myShowTuple

 myShowTuple :: MyShow b => (a, b) -> String 

Otherwise, you have no way for the actual format, which is b , which in the end is of any type.

That would do

 instance (MyShow a) => MyShow (a, b) where ... 

in

 instance (MyShow a, MyShow b) => MyShow (a, b) where ... 
+3
source

Source: https://habr.com/ru/post/946353/


All Articles