I am a Haskell beginner and have a weird question. Up until now everything has been going great, and I have been able to use Prelude read function normally. Now suddenly I have to constantly declare it's type in order to use it.
I always have to declare this or something similar in order to use it.
let r = read::String-> Int
I have tried restarting ghci thinking I have accidentally overloaded read but whenever I try using it normally as such
read "456"
I get the following error
No instance for (Read a0) arising from a use of `read'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Read () -- Defined in `GHC.Read'
instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
instance (Read a, Read b, Read c) => Read (a, b, c)
-- Defined in `GHC.Read'
...plus 25 others
In the expression: read "456"
In an equation for `it': it = read "456"
Does anyone have any ideas what could be causing this and how to fix it?
First, to solve your problem, you could specify the result you expect from read
in one of a few ways:
First, by specifying the type of the result of the entire computation:
read "456" :: Int
Or by specifying the type of the function read:
(read :: String -> Int) "456"
To explain why this happens, the issue is that read
is polymorphic in its result type. That is, there are many different read
functions defined, and they are defined as part of a typeclass Read
. The error gives you the explanation why:
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Read () -- Defined in `GHC.Read'
instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
instance (Read a, Read b, Read c) => Read (a, b, c)
-- Defined in `GHC.Read'
...plus 25 others
The Read
typeclass is defined for many, many types, it says there are 28 instances defined in the error. It's defined for Int, and for Integer, and Double, and String, and countless others. And so in order for the compiler to know what instance of the typeclass to use, it needs to be able to infer that from the type of read
that you specify. If you give it :: String -> Int
, the compiler can figure it out.
To understand how this works, let's look at part of the type class for Read
:
class Read a where
read :: String -> a
So for a given type a
, an instance of Read a
must be defined in order for the function read
to return a value of type a
. So for Int
, there is a definition for an instance of Read Int
, and indeed there is in GHC-Read. That instance defines how read
works. Unfortunately, the instance is rather obtuse and relies on other functions and other such things, but for the sake of completeness, here it is:
instance Read Int where
readPrec = readNumber convertInt
readListPrec = readListPrecDefault
readList = readListDefault
But, the important thing to realize is that this instance means that read
will work for Int
. But shortly after, there's this instance:
instance Read Double where
readPrec = readNumber convertFrac
readListPrec = readListPrecDefault
readList = readListDefault
Oh dear. So read
works for Int
and for Double
!
The Haskell language ensures the compiler will, usually, try to infer a unique type for a value or fail. There are some odd exceptions to this with some extensions to the language in GHC, but you have to remember here that Haskell is trying to prevent you from shooting yourself in the foot. If you don't specify somehow the type you're expecting from read
, then the compiler could infer any type, but that might not be valid. You wouldn't want your code to suddenly start reading in floating point Double
s or arbitrary precision Integer
s where you previously expected to read in an Int
, right?
In this case, not specifying the type merely caused the compiler to give up, it couldn't definitively answer the question you asked it to, which is, "Which instance of Read
do I use?" And Haskell compilers do not like guessing. One way to prevent it from guessing is to use a value in a way that is unambiguous later, or to define a function that uses read
unambiguously. For example, this function can be used in your code to avoid having to type :: String -> Int
:
readInt :: String -> Int
readInt = read
The compiler can figure out which instance of Read
it needs to use there, and in your code if you do:
readInt "456"
The compiler will know that readInt
has type String -> Int
, and thus readInt "456"
has type Int
. Unambiguous, just like Haskell likes it.
The syntax in GHCi is a bit different for defining things, so if you are using GHCi exclusively to play around with Haskell, I'd recommend you switch to loading .hs files and using the :r
command to reload them. One problem with GHCi is that you have to define everything with "let" which will start to feel strange when you start writing programs, where top level definitions don't need that. Another problem is the monomorphism restriction which, frankly, is just kind of weird and quirky and it's not worth going into detail about it here.
Anyhow, the syntax for defining readInt
above in GHCi is simple, though there are two ways (equivalent). You already know one, which is to do it like this:
let readInt = read :: String -> Int
The other way is to define a function and its type separately, which is more akin to how it is done in a regular .hs file:
let readInt :: String -> Int; readInt = read
Neither is idiomatic Haskell, but that's because GHCi imposes quirky limitations on writing code and multi-line entry is strange. You could do this:
:{
let readInt :: String -> Int;
readInt = read
:}
And that will get you close to the idiomatic Haskell definition. GHCi will however correctly compile my initial example if you put it in an .hs file and use :load
or specify the file with ghci ./somefile.hs
.
When you type
read "456"
at the prompt, ghci has no information to find out which type you want. Do you want an Int
, a Bool
, ()
, String
, ...
You need to tell it,
read "456" :: Int
so that it knows.
In a real programme, usually there is context which determines the required type, then it can be inferred and you don't need to manually supply a type. At the prompt, there is no context to help the inference.
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