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.
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 =>orShow Car =>is not helpful, and the error message tells you this is becauseStringhas no type-variable (which always start with a lower-case letter). This is because eitherinstance Show Stringis visible in the scope oftellCaror 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 -> Stringviolates this sinceais the only type variable on the RHS of the => andShow zdoes not mentiona. ThisShow zdoes not constrain the type variablea.More specifically for your case you write
deriving (Show)which auto-generated an instance:The auto-generated code only works if there are
Showinstances forp,q,r. Your specialization tomentions
Car String String a. UsingShowonCar String String aintellCarselects the auto-generated instance forShow (Car p q r)and creates a need forShow StringandShow a. The compiler then sees an instance forShow Stringfrom the implicitly importedPreludemodule leaving only the danglingShow aconstraint. This constraint onainfects the type oftellCar.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”.