为什么 httpJSON 失败,而 httpLBS 成功?
Why does httpJSON fail, but httpLBS succeeds?
此功能(使用 httpLBS)有效:
makeRequest = do
response <- httpLBS "http://httpbin.org/get"
putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)
但是这个函数(使用 httpJSON)不会:
makeRequest = do
response <- httpJSON "http://httpbin.org/get"
putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)
它抛出错误:
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.
比较httpLBS
和httpJSON
的类型:
httpLBS :: MonadIO m => Request -> m (Response ByteString)
httpJSON :: (MonadIO m, FromJSON a) => Request -> m (Response a )
请注意 httpLBS
总是 生成 Response ByteString
,但 httpLBS
生成 Response a
。这是什么意思?
在这种情况下,这意味着 httpJSON
可以生成一个 Response
包含任何具有 FromJSON
实例的东西,这取决于函数的 调用者 来决定。来电者如何决定?通过指定类型!这是 Haskell 类型类最有趣的属性之一:程序的行为由其 类型.
决定
当然,大多数时候,您看不到这些类型,因为它们是推断出来的。例如,如果您编写以下程序,则不需要编写任何类型注释:
ghci> id True
True
即使 id
函数的类型为 a -> a
,GHC 也可以推断出 a
显然只有 一个 选项,Bool
,所以选择了。但是,考虑一下您的程序——GHC 怎么知道 a
应该是什么? response
结果仅用在一个地方,getResponseStatusCode
,具有以下类型签名:
getResponseStatusCode :: Response a -> Int
这个函数也适用于 any Response a
,所以 GHC 仍然不能决定 a
应该是什么:根据 GHC 的术语, a
变量 不明确 。问题是必须为 a
选择特定类型,因为它需要知道使用哪个 FromJSON
实例来解析响应主体。
为了解决这个问题,您可以通过提供自己的类型注释来消除表达式的歧义,强制 GHC 为 a
选择特定类型:
makeRequest = do
response <- httpJSON "http://httpbin.org/get" :: IO (Response ())
putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)
当然,您应该将 ()
替换为代表您希望响应产生的 JSON 结构的任何类型。
不确定这是否对其他人有帮助,但对我有帮助。我尝试了 ::IO (Response ())
注释,我得到了完整的打印响应,然后 "expected (), encountered Object"
,并将其切换为 :: IO (Response Object)
修复它以简单地输出
λ makeRequest
200
it :: ()
此功能(使用 httpLBS)有效:
makeRequest = do
response <- httpLBS "http://httpbin.org/get"
putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)
但是这个函数(使用 httpJSON)不会:
makeRequest = do
response <- httpJSON "http://httpbin.org/get"
putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)
它抛出错误:
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.
比较httpLBS
和httpJSON
的类型:
httpLBS :: MonadIO m => Request -> m (Response ByteString)
httpJSON :: (MonadIO m, FromJSON a) => Request -> m (Response a )
请注意 httpLBS
总是 生成 Response ByteString
,但 httpLBS
生成 Response a
。这是什么意思?
在这种情况下,这意味着 httpJSON
可以生成一个 Response
包含任何具有 FromJSON
实例的东西,这取决于函数的 调用者 来决定。来电者如何决定?通过指定类型!这是 Haskell 类型类最有趣的属性之一:程序的行为由其 类型.
当然,大多数时候,您看不到这些类型,因为它们是推断出来的。例如,如果您编写以下程序,则不需要编写任何类型注释:
ghci> id True
True
即使 id
函数的类型为 a -> a
,GHC 也可以推断出 a
显然只有 一个 选项,Bool
,所以选择了。但是,考虑一下您的程序——GHC 怎么知道 a
应该是什么? response
结果仅用在一个地方,getResponseStatusCode
,具有以下类型签名:
getResponseStatusCode :: Response a -> Int
这个函数也适用于 any Response a
,所以 GHC 仍然不能决定 a
应该是什么:根据 GHC 的术语, a
变量 不明确 。问题是必须为 a
选择特定类型,因为它需要知道使用哪个 FromJSON
实例来解析响应主体。
为了解决这个问题,您可以通过提供自己的类型注释来消除表达式的歧义,强制 GHC 为 a
选择特定类型:
makeRequest = do
response <- httpJSON "http://httpbin.org/get" :: IO (Response ())
putStrLn $ "The status code was: " ++ show (getResponseStatusCode response)
当然,您应该将 ()
替换为代表您希望响应产生的 JSON 结构的任何类型。
不确定这是否对其他人有帮助,但对我有帮助。我尝试了 ::IO (Response ())
注释,我得到了完整的打印响应,然后 "expected (), encountered Object"
,并将其切换为 :: IO (Response Object)
修复它以简单地输出
λ makeRequest
200
it :: ()