Reputation: 8476
I am just beginning to learn Haskell and am following the book "Learnyouahaskell".I have come across this example
tell :: (Show a) => [a] -> String
tell [] = "The list is empty"
I understand that (Show a)
here is a class constraint and the type of parameter , in this case a
has to be able to be "showable" .
Considering that a
here is a list and not an element of the list , why am i unable to declare the function like :-
tell :: (Show a) =>a->String
Edit 1:-from the answers below i seem to understand that one would need to specify the concrete type of a
for pattern matching. Considering this,what would be a correct implementation of the below:-
pm :: (Show a) =>a->String
pm 'g'="wow"
It gives me the error as below
Could not deduce (a ~ Char)
from the context (Show a)
bound by the type signature for pm :: Show a => a -> String
at facto.hs:31:7-26
`a' is a rigid type variable bound by
the type signature for pm :: Show a => a -> String at facto.hs:31:7
In the pattern: 'g'
In an equation for `pm': pm 'g' = "wow"
Failed, modules loaded: none.
I understand from the error message that it s not able to deduce the concrete type of a
, but then how can it be declared using Show
.
I know I can solve the above like this:-
pmn :: Char->String
pmn 'g'="wow"
But I am just trying to understand the Show
typeclass properly
Upvotes: 4
Views: 3057
Reputation: 71450
tell :: (Show a) =>a->String
This says tell
accepts a value of any type a
that is showable. You can call it on anything showable. Which implies that inside the implementation of tell
, you have to be able to operate on anything at all (that is showable).
You might think that this would be an okay implementation for that type signature:
tell [] = "The list is empty"
Because lists are indeed showable, and so are valid values for the first parameter. But there I'm checking whether the argument is an empty list; only values of the list type can be matched against list patterns (such as the empty list pattern), so this doesn't make sense if I'd called tell 1
or tell True
or tell (1, 'c')
, etc.
Inside tell
, that type a
could be any type that is an instance of Show
. So the only things I can do with that value are things that are valid to do with all types that are instances of Show
. Which basically means you can only pass it to other similar functions with a generic Show a => a
parameter.1
Your confusion is stemming from this misconception "Considering that a here is a list and not an element of the list" about the type signature tell :: (Show a) => [a] -> String
. Here a
is in fact an element of the list, not the list itself.
That type signature reads "tell takes a single paramter, which is a list of some showable type, and returns a string". This version of tell
knows it receives a list, so it can do listy things with its argument. It's the things inside the list which are members of some unknown type.
1 Most of those functions will also be unable to do anything with the value other than pass it on to another Show
function, but sooner or later the value will either be ignored or passed to one of the actual functions in the Show
typeclass; these have specialised implementations for each type so each specialised version gets to know what type it's operating on, which is the only way anything can eventually be done.
Upvotes: 1
Reputation: 33637
List does implement Show
type class but when you say: Show a => a -> String
It means the function will accept any type which implements Show
AND most importantly you can only call show class functions on a nothing else, your function will never know the concrete type of a
. Whereas you are trying to call list pattern matching on a
Update for new edit in question:
The correct implementation would be: pm c ="wow"
. You can call any Show
type class functions on parameter c
. You cannot pattern match as you were trying before because you dont know the exact type of parameter, you only know that it implements Show
type class. But when you specific Char
as the type then the pattern matching works
Upvotes: 3
Reputation: 21
As soon as you pattern match on 'g', eg
pm 'g' = "wow"
your function no longer has a type of (Show a) => a -> String
; instead it has has a concrete type for 'a', namely Char, so it becomes Char -> String
This is in direct conflict with the explicit type signature you gave it, which states your function works with any type 'a' (as long as that type is an instance of Show
).
You can't pattern match in this case, since you are pattern matching on an Int, Char, etc. But you can use the show
function in the Prelude:
pm x = case show x of
"'g'" -> "My favourite Char"
"1" -> "My favourite Int"
_ -> show x
As you may have guessed, show
is a bit magical ;). There's actually a whole bunch of show
functions implemented for each type that is an instance of the Show
typeclass.
Upvotes: 2
Reputation: 14578
The problem isn't with Show
. Indeed if we try:
tell2 :: a -> String
tell2 [] = "The list is empty"
We get a type check error. Lets see what it says:
test.hs:5:7:
Couldn't match expected type `a' with actual type `[t0]'
`a' is a rigid type variable bound by
the type signature for tell2 :: a -> String at test.hs:4:10
In the pattern: []
In an equation for `tell2': tell2 [] = "The list is empty"
Now we ask ourselves, what is this does this so-called 'type' construct really mean? When you write tell2 :: a -> String
, you are saying is that for any type that is exactly a, tell2
will give us a String
. [a]
(or [c]
or [foo]
- the name doesn't matter) is not exactly a
. This may seem like an arbitrary distinction, and as far as I know, it is. Let's see what happens when we write
tell2 [] = "The list is empty"
> :t tell2
> tell2 :: [t] -> [Char]
As you well know there is no difference between writing t
and a
, and [Char]
is just a type synonym for String
, so the type we wrote and the type GHC infers are identical.
Well, not quite. When you, yourself, the programmer, specify the type of a function manually in your source, the type variables in your type signature become rigid. What does that mean exactly?
from https://research.microsoft.com/en-us/um/people/simonpj/papers/gadt/:
"Instead of "user-specified type", we use the briefer term rigid type to describe a type that is completely specified, in some direct fashion, by a programmer-supplied type annotation."
So a rigid type is any type specified by a programmer type signature. All other types are "wobbly"[1]
So, just by virtue of the fact that you wrote it out, the type signature has become different. And in this new type grammar, we have that a /= [b]
. For rigid type signatures, GHC will infer the very least amount of information it can. It must infer that a ~ [b]
from the pattern binding; however it cannot make this inference from the type signature you have provided.
Lets look at the error GHC gives for the original function:
test.hs:2:6:
Could not deduce (a ~ [t0])
from the context (Show a)
bound by the type signature for tell :: Show a => a -> String
at test.hs:1:9-29
`a' is a rigid type variable bound by
We see again rigid type variable
etc., but in this case GHC also claims it could not deduce something. (By the way - a ~ b === a == b
in the type grammar). The type checker is actually looking for a constraint in the type that would make the function valid; it doesn't find it and is nice enough to tell you exactly what it would need to make it valid:
{-# LANGUAGE GADTs #-}
tell :: (a ~ [t0], Show a) => a -> String
tell [] = "The list is empty"
If we insert GHC's suggestion verbatim, it type checks, since now GHC doesn't need to make any inferences; we have told it exactly what a
is.
Upvotes: 3
Reputation: 8930
In both signatures, a
isn't a list -- it's any type at all, and you don't get to pick which (except that it must be an instance of Show
).
In
tell₁ :: Show a => [a] -> String
tell₁ [] = "The list is empty"
... -- (remember to match the non-empty list case too!)
You're matching on the list of a
s, not on a value of type a
itself.
If you wrote
tell₂ :: Show a => a -> String
tell₂ [] = "The list is empty"
...
You would be assuming that the type a
is the type of lists (of something). But it could be any type at all, such as Bool
.
(But it's possible that I don't understand your question -- you haven't really said what the problem is. When asking a question like this you should generally specify what you did, what you expected, and what happened. None of these is really specified here, so people can only guess at what you might've meant.)
Upvotes: 3