5

I'm still new to Haskell and am having trouble to find the correct type signature for a function.

I have this working, fairly straightforward function using http-conduit and authenticate-oauth which is side-effecting, so I don't care too much about the return value:

executeOAuthRequest oauth cred request =
 withManager $ \manager -> do
 signed <- signOAuth oauth cred request
 http signed manager

I would like to specify the correct type signature, but the output from GHCi looks rather terrifying to me:

executeOAuthRequest
 :: (monad-control-0.3.2.3:Control.Monad.Trans.Control.MonadBaseControl
 IO m,
 Control.Monad.Trans.Resource.Internal.MonadThrow m,
 Control.Monad.Trans.Resource.Internal.MonadUnsafeIO m,
 Control.Monad.IO.Class.MonadIO m) =>
 OAuth
 -> Credential
 -> Request
 -> m (Response (ResumableSource (ResourceT m) ByteString))

The first three arguments (OAuth, Credential, Request) make sense to me, but I don't understand the long precondition for m and wonder if it's necessary to specify the full return value as GHCi suggests.

Rather than only providing the correct signature, I'd like to understand what the process behind finding and reducing the correct one looks like.

asked Apr 21, 2014 at 16:51
4
  • 3
    Note that IO implements all those constraints, so if you only need to use IO, you can just drop the constraint and specialize m to IO like this: OAuth -> Credential -> Request -> IO (Respond (ResumableSource (ResourceT IO) ByteString)) Commented Apr 21, 2014 at 17:07
  • @GabrielGonzalez Thank you! Is this because IO is * -> * or is there another property because of which it satisfies all constraints? Commented Apr 21, 2014 at 17:17
  • 2
    @passy no. * -> * is a kind. It only means that IO is a type constructor (and IO a is a type for any type a). IO statisfies all constraints because the typeclasses instances are declared. as an example, IO has a MonadIO instance. The more complex type signature is useful because there are other monads which acts like IO, and which can be used instead. Commented Apr 21, 2014 at 17:33
  • 1
    @passy The idea is that those constraints make the type more flexible so that your function can be used with monads other than IO. However, if you don't need that extra flexibility you can simplify the type by choosing a specific monad that implements those constraints. Commented Apr 21, 2014 at 17:41

1 Answer 1

8

GHCi is giving you the most polymorphic signature, reading "any type m which is an instance of the classes MonadThrow from the Monad.Trans.Resource.Internal module, MonadIO, etc". The type-checker is sort of taking the union of all the constraints introduced by the types of those polymorphic functions you're composing in executeOAuthRequest.

Here's a simple example:

Prelude> let f n = n + 1
Prelude> :t f -- GHC infers the polymorphic signature constrained to `Num`:
f :: Num a => a -> a
Prelude> let f :: Int -> Int ; f n = n + 1
Prelude> :t f
f :: Int -> Int

It might be that the only type which satisfies all those constraints in your function is IO, or there might be several. The usual way to check is by reading the haddocks, or doing e.g.

Prelude> :info IO
...
instance Monad IO -- Defined in ‘GHC.Base’
instance Functor IO -- Defined in ‘GHC.Base’
Prelude> :info Monad
...
instance Monad [] -- Defined in ‘GHC.Base’
instance Monad IO -- Defined in ‘GHC.Base’
instance Monad ((->) r) -- Defined in ‘GHC.Base’

So if you only use your function in IO you can give it the type:

executeOAuthRequest
 :: OAuth
 -> Credential
 -> Request
 -> IO (Response (ResumableSource (ResourceT m) ByteString))

or you could keep the polymorphic signature, if for instance you're exporting this function in a library and think your users might want to use it in a different monad (maybe even by defining their own new type and making it an instance of MonadIO, MonadThrow, etc)

answered Apr 21, 2014 at 17:41
Sign up to request clarification or add additional context in comments.

1 Comment

Terrific explanation, thank you. The note about the library is really interesting. I naively assumed that GHC's output is just noisy and reducing the signature would always be what you want, but haven't thought about the possibility that someone might want to use a different monad.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.