Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does httpJSON fail, but httpLBS succeeds?

This function (with httpLBS) works:

makeRequest = do
  response <- httpLBS "http://httpbin.org/get" 
  putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)

But this function (with httpJSON) does not:

makeRequest = do
  response <- httpJSON "http://httpbin.org/get" 
  putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)

It throws the error:

Ambiguous type variable `a0' arising from a use of `httpJSON' prevents the constraint 
`(aeson-1.1.2.0:Data.Aeson.Types.FromJSON.FromJSON a0)' from being solved.
          Probable fix: use a type annotation to specify what `a0' should be.
like image 589
TomDane Avatar asked Oct 11 '17 17:10

TomDane


1 Answers

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.

like image 99
Alexis King Avatar answered Nov 21 '22 08:11

Alexis King