Search code examples
haskelltype-inferencetype-variables

How do I specify a type variable used in an inline type, is the same as a type variable used in a function definition?


(Apologies if my terminology is wrong).

I'm trying to write a wrapper function that handles exceptions: if the given IO action throws, it returns Nothing (inside an IO context of course), but if the given IO action succeeds, it returns Just v.

tryMaybe :: IO a -> IO (Maybe a)
tryMaybe action = do
    result <- (try action) :: IO (Either SomeException a)
    return $ case result of
        Left _  -> Nothing
        Right v -> Just v

This results in the compiler error message:

  Couldn't match type `a' with `a1'
      `a' is a rigid type variable bound by
          the type signature for tryMaybe :: IO a -> IO (Maybe a)
          at src/Database.hs:33:13
      `a1' is a rigid type variable bound by
           an expression type signature: IO (Either SomeException a1)
           at src/Database.hs:35:15
    Expected type: IO a1
      Actual type: IO a
    In the first argument of `try', namely `action'

I'm guessing that the type variable a in the first line isn't the same as the a in the third line - they just happen to have the same name in the source code, and the compiler has renamed it a1 in the error message.

So how do I tell Haskell that these are the same types?


Solution

  • You need to enable the ScopedTypeVariables extension, and the change the type signature of the top-level function to start with forall a .:

    {-# LANGUAGE ScopedTypeVariables #-}
    
    ...
    
    tryMaybe :: forall a . IO a -> IO (Maybe a)
    ...
    

    The forall syntax brings the type variable a into scope for the entire body of tryMaybe, rather than it being limited to the type signature which is the default. This is mainly a historical anomaly rather than a deliberate design.