Compare the types of httpLBS
and httpJSON
:
httpLBS :: MonadIO m => Request -> m (Response ByteString)
httpJSON :: (MonadIO m, FromJSON a) => Request -> m (Response a )
Notice that httpLBS
always produces a Response ByteString
, but httpLBS
produces Response a
. What does that mean?
In this case, it means that httpJSON
can produce a Response
containing anything at all with a FromJSON
instance, and it’s up to the function’s caller to decide. How does the caller decide? By specifying the type! This is one of the most interesting properties of Haskell’s typeclasses: the behavior of your program is determined by its types.
Of course, most of the time, you don’t see those types, because they are inferred. For example, if you write the following program, you will not need to write any type annotations:
ghci> id True
True
Even though the id
function has type a -> a
, GHC can infer that there is clearly only one choice for a
, Bool
, so it is chosen. However, consider your program—how can GHC know what a
is supposed to be? The response
result is only used in one place, getResponseStatusCode
, which has the following type signature:
getResponseStatusCode :: Response a -> Int
This function also works on any Response a
, so GHC still can’t decide what the a
should be: according to GHC’s terminology, the a
variable is ambiguous. The trouble is that picking a specific type for a
is necessary, since it needs to know which FromJSON
instance to use to parse the response body.
In order to solve this problem, you can disambiguate the expression by supplying your own type annotation, forcing GHC to pick a particular type for a
:
makeRequest = do
response <- httpJSON "http://httpbin.org/get" :: IO (Response ())
putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)
Of course, you should replace ()
with whatever type represents the structure of the JSON you expect the response to produce.