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.