Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 489
stusmith Avatar asked Dec 25 '22 07:12

stusmith


1 Answers

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.

like image 149
GS - Apologise to Monica Avatar answered May 12 '23 15:05

GS - Apologise to Monica