Question

I am new to Haskell and am trying to a sample code of http-conduit version 2.0.0.4, but it dose not work

Here is the sample code

{-# 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 ())

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

the error message when I load it

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.

How can I fix it?

Many Thanks

Update

Following Abrahamson's advice, I have changed my code little bit to the following and now have proper StatusCodeException handling.

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))
Was it helpful?

Solution

You're not using E.catch as it was intended. If you take a look at the type:

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

it's clear that the return type of the first and second arguments must match. In your case you have

withManager $ httpLbs request :: IO (Response ByteString)

in the first branch and either

putStrLn "login failed" -- or
return ()

in the second. These types do not match and thus you're getting the error you see.


In higher level terms, the problem is that you're not handling the success case. For instance, we could rewrite this using E.try to make that more clear

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))

Here since I explicitly pattern match on the Either StatusCodeException (Response ByteString) it's clear that I needed to provide both the failing and the succeeding branche and give them the same return types. To do so I introduced an action to perform on the successful case.

Generally, I find E.try easier to use. E.catch is primarily useful when you want to provide a default under failure.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top