I am working on OAuth2 authentication for a Yesod application and I am having a type error that I really really don't understand. The code is broken at the moment, and I have a few :: IO ()'s and undefineds thrown around to help me isolate the type error, but the relevant code is:
getAccessToken :: Manager -> OAuth2 -> ExchangeToken -> IO (OAuth2Result Errors OAuth2Token)
getAccessToken manager oa code = do
let (uri, defaultBody) = accessTokenUrl oa code
let body = defaultBody <> [ ("client_id", TE.encodeUtf8 . oauthClientId $ oa )
, ("client_secret", TE.encodeUtf8 . oauthClientSecret $ oa)
, ("resource", TE.encodeUtf8 . oauthClientId $ oa)
]
response <- performOAuth2PostRequest manager oa uri body
return undefined
performOAuth2PostRequest :: Manager -> OAuth2 -> URI -> PostBody -> IO (Response ByteString)
performOAuth2PostRequest manager oa uri body = do
defaultReq <- uriToRequest uri
let addBasicAuth = applyBasicAuth (TE.encodeUtf8 . oauthClientId $ oa)
(TE.encodeUtf8 . oauthClientSecret $ oa)
let req = (addBasicAuth . updateRequestHeaders Nothing) defaultReq
(httpLbs (urlEncodedBody body req) manager) :: IO (Response ByteString)
Notice that I am specifically setting the type of the httpLbs (urlEnc...) manager action as an IO (Response ByteString) using the ScopedTypeVariables extension. Also, that line of code should be an IO action because it's being performed at the top level of an IO action.
In fact, I ran a GHCi session and did:
Network.OAuth.OAuth2.HttpClient Network.OAuth.OAuth2.Internal
Network.HTTP.Conduit Data.Functor Prelude> :t httpLbs
httpLbs
:: Control.Monad.IO.Class.MonadIO m =>
Request
-> Manager -> m (Response Data.ByteString.Lazy.Internal.ByteString)
Which confirms my understanding that httpLbs should yield a MonadIO m => m (Response ByteString).
But here is the error I get:
• Couldn't match type ‘Response
Data.ByteString.Lazy.Internal.ByteString’
with ‘IO (Response ByteString)’
Expected type: Manager -> IO (Response ByteString)
Actual type: Manager
-> Response Data.ByteString.Lazy.Internal.ByteString
• The function ‘httpLbs’ is applied to two arguments,
its type is ‘Request
-> m1 (Response Data.ByteString.Lazy.Internal.ByteString)’,
it is specialized to ‘Request
-> Manager -> Response Data.ByteString.Lazy.Internal.ByteString’
Why is GHC specializing m to Response instead of IO? How do I fix it?
ByteStringwhile the actual type is a lazyByteString(which the compiler helpfully indicates is a different type by printing it fully qualified). The error seems bogus, however, since there is no way to specialize the type ofhttpLbstoRequest -> Manager -> Response Lazy.ByteString- even if you did setm ~ Response(which would give another very obvious error -no instance for MonadIO Response) then the actual instantiation would be... -> Response (Response Lazy.ByteString).ScopedTypeVariablesbecauseIO (Response ByteString)doesn't contain any type variables; and it is also entirely superfluous, since the type signature on the function already gives the type.