0

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?

2
  • Based on what I can tell, the actual problem is the expected type is a strict ByteString while the actual type is a lazy ByteString (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 of httpLbs to Request -> Manager -> Response Lazy.ByteString - even if you did set m ~ Response (which would give another very obvious error - no instance for MonadIO Response) then the actual instantiation would be ... -> Response (Response Lazy.ByteString). Commented Nov 29, 2017 at 22:29
  • Aside: the type annotation you gave in the final line doesn't make use of ScopedTypeVariables because IO (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. Commented Nov 29, 2017 at 22:31

1 Answer 1

1

You haven't included your import statements, making it difficult to debug this. My best guess though is that you've imported Network.HTTP.Simple, which provides functions that do not require an explicit Manager argument. I'm guessing this from the error message providing the expected type:

Request -> m1 (Response Data.ByteString.Lazy.Internal.ByteString)

Solution: either change the import, or drop the Manager argument.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.