Search code examples
haskellghchaskell-platform

Unable to understand coding logic / principle / convention - why 'Show a' is needed? Why 'Show Car" or "Show String" is not working?


Code given below compiles, ok.

data Car p q r = Car {company :: p  
                     , model :: q  
                     , year ::r  
                     } deriving (Show)


tellCar :: (Show a) => Car String String a -> String

What are the basic principles/ conventions / logic which can remind me that I need to take 'Show a' only in 'tellCar', not any other option? Where can I find resource to learn such principles/ conventions / logic?

If I mistakenly take 'Show Car' in the tellCar, following error message is received on compilation:

*Main> :load "/home/optimight/baby.hs"  
[1 of 1] Compiling Main             ( /home/optimight/baby.hs, interpreted )  

/home/optimight/baby.hs:96:18:  
    Expecting three more arguments to `Car'  
    In the type signature for `tellCar':  
      tellCar :: Show Car => Car String String a -> String  
Failed, modules loaded: none.  

If I mistakenly take 'Show z' in the tellCar, following error message is received on compilation:

*Main> :load "/home/optimight/baby.hs"  
[1 of 1] Compiling Main               ( /home/optimight/baby.hs, interpreted )

/home/optimight/baby.hs:96:1:  
    Ambiguous constraint `Show z'  
      At least one of the forall'd type variables mentioned by the constraint  
      must be reachable from the type after the '=>'  
    In the type signature for `tellCar':  
      tellCar :: Show z => Car String String a -> String  
Failed, modules loaded: none.   

If I mistakenly take 'Show String' in the tellCar, following error message is received on compilation:

Prelude> :load "/home/optimight/baby.hs"  
[1 of 1] Compiling Main             ( /home/optimight/baby.hs, interpreted )  

/home/optimight/baby.hs:96:1:  
    Non type-variable argument in the constraint: Show String  
    (Use -XFlexibleContexts to permit this)  
    In the type signature for `tellCar':  
      tellCar :: Show String => Car String String a -> String  
Failed, modules loaded: none.  

Solution

  • Main idea: Each constraint listed before the => in the type signature is there to constrain one or more type-variables to the right of the => in the type signature.

    Principle: Constraints in type signatures always have a type variable in them somewhere.

    Writing Show String => or Show Car => is not helpful, and the error message tells you this is because String has no type-variable (which always start with a lower-case letter). This is because either instance Show String is visible in the scope of tellCar or it is not, and you never need to list fully concrete instances as contraints in the type.

    Principle: The constraint you list has to mention at least one of the type variable to the right of the => in the type signature. With LANGUAGE extensions the constraint may mention zero or more extra type variables that only exist on the left of the => in the type signature.

    Writing tellCar :: Show z => Car String String a -> String violates this since a is the only type variable on the RHS of the => and Show z does not mention a. This Show z does not constrain the type variable a.

    More specifically for your case you write deriving (Show) which auto-generated an instance:

    instance (Show p, Show q, Show r) => Show (Car p q r) where
      showsPrec = ...
    

    The auto-generated code only works if there are Show instances for p,q,r. Your specialization to

    tellCar :: (Show a) => Car String String a -> String
    

    mentions Car String String a. Using Show on Car String String a in tellCar selects the auto-generated instance for Show (Car p q r) and creates a need for Show String and Show a. The compiler then sees an instance for Show String from the implicitly imported Prelude module leaving only the dangling Show a constraint. This constraint on a infects the type of tellCar.

    The resource to learn about the Haskell type system is one of the books on Haskell.

    EDIT: The precise part of the Haskell 98 Report covering this seems to section 4.1.3. More background is in "A History of Haskell".