3

我是 Haskell 的新手,正在尝试 http-conduit 版本 2.0.0.4 的示例代码,但它不起作用

这是示例代码

{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Conduit
import Network
import Data.Time.Clock
import Data.Time.Calendar
import qualified Control.Exception as E

past :: UTCTime
past = UTCTime (ModifiedJulianDay 56200) (secondsToDiffTime 0)

future :: UTCTime
future = UTCTime (ModifiedJulianDay 562000) (secondsToDiffTime 0)

cookie :: Cookie
cookie = Cookie { cookie_name = "password_hash"
               , cookie_value = "abf472c35f8297fbcabf2911230001234fd2"
               , cookie_expiry_time = future
               , cookie_domain = "example.com"
               , cookie_path = "/"
               , cookie_creation_time = past
               , cookie_last_access_time = past
               , cookie_persistent = False
               , cookie_host_only = False
               , cookie_secure_only = False
               , cookie_http_only = False
               }

main = withSocketsDo $ do
    request' <- parseUrl "http://example.com/secret-page"
    let request = request' { cookieJar = Just $ createCookieJar [cookie] }
    E.catch (withManager $ httpLbs request)
            (\(StatusCodeException statusCode _ _) ->
              if statusCode==403 then putStrLn "login failed" else return ())

参考:http ://hackage.haskell.org/package/http-conduit-2.0.0.4/docs/Network-HTTP-Conduit.html

我加载它时的错误消息

samplecode.hs:33:39:
    Couldn't match type `()'
                  with `Response Data.ByteString.Lazy.Internal.ByteString'
    Expected type: IO
                     (Response Data.ByteString.Lazy.Internal.ByteString)
      Actual type: IO ()
    In the return type of a call of `putStrLn'
    In the expression: putStrLn "login failed"
    In the expression:
  if statusCode == 403 then putStrLn "login failed" else return ()

samplecode.hs:33:75:
    Couldn't match expected type `Response
                                    Data.ByteString.Lazy.Internal.ByteString'
                with actual type `()'
    In the first argument of `return', namely `()'
    In the expression: return ()
    In the expression:
      if statusCode == 403 then putStrLn "login failed" else return ()
Failed, modules loaded: none.

我该如何解决?

非常感谢

更新

按照 Abrahamson 的建议,我将代码稍微更改为以下内容,现在可以正确处理 StatusCodeException。

main = withSocketsDo $ do
    request' <- parseUrl "http://example.com/secret-page"
    let request = request' { cookieJar = Just $ createCookieJar [cookie] }
    eitherResp <- E.try (withManager $ httpLbs request)
    case eitherResp of
      Left (StatusCodeException s _ _) 
        | statusCode s == 403 -> putStrLn "login failed"
        | otherwise         -> return () 
      Right resp -> print (L.length (responseBody resp))
4

1 回答 1

2

您没有E.catch按预期使用。如果你看一下类型:

E.catch :: Exception e => IO a -> (e -> IO a) -> IO a

很明显,第一个和第二个参数的返回类型必须匹配。在你的情况下,你有

withManager $ httpLbs request :: IO (Response ByteString)

在第一个分支中,或者

putStrLn "login failed" -- or
return ()

在第二。这些类型不匹配,因此您会看到您看到的错误。


在更高级别的术语中,问题在于您没有处理成功案例。例如,我们可以重写它使用E.try以使其更清晰

eitherResp <- E.try (withManager $ httpLbs request)
case eitherResp of
  Left (StatusCodeException statusCode _ _)
    | statusCode == 403 -> putStrLn "login failed"
    | otherwise         -> return ()
  Right resp -> print (ByteString.length (responseBody resp))

在这里,由于我明确地进行了模式匹配,Either StatusCodeException (Response ByteString)很明显我需要同时提供失败的分支和后续的分支,并为它们提供相同的返回类型。为此,我引入了对成功案例执行的操作。

一般来说,我觉得E.try更容易使用。E.catch当您想在失败时提供默认值时,它主要有用。

于 2014-01-27T16:48:57.220 回答