Reputation: 33
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))
Upvotes: 3
Views: 191
Reputation: 74334
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.
Upvotes: 2