Fraser
Fraser

Reputation: 1521

Monomorphism restriction in pattern bindings

{-# LANGUAGE NoMonomorphismRestriction #-}
module Try where

f :: IO (a -> IO String)
f = return $ const getLine

main :: IO ()
main = do
  g <- f
    :: IO (a -> IO String)
  g "String" >>= print
  g 5 >>= print

Even with the NoMonomorphismRestriction flag and explicit type signature, this module fails to compile with Couldn't match expected type ‘[Char]’ with actual type ‘Int’, despite g being fully polymorphic.

Upvotes: 2

Views: 73

Answers (1)

luqui
luqui

Reputation: 60513

This is not what the monomorphism restriction means. The monomorphism restriction says that if a definition has no type signature and has a left-hand side with no parameters, it will be specialized to a monomorphic type (or rather just monomorphic enough to get rid of any class constraints). You have given type signatures so it doesn't apply.

The problem here is that you have given the wrong type to f.

f :: IO (a -> IO String)

actually means

f :: forall a. IO (a -> IO String)

That is, first pick a type a, then you can bind to get a monomorphic function of type a -> IO String for that a. There is no trouble with this program, for example:

main = do
    g <- f
    g "String" >>= print
    g' <- f
    g' 5 >>= print

But your usage example requires this type:

f :: IO (forall a. a -> IO String)

That is, you want to bind first and pick the type later, i.e. use the function at multiple types. This is called an "impredicative type" and unfortunately GHC has not supported them for quite a while, as far as I know.

The way to solve this problem is to make a newtype wrapper that explicitly quantifies the inner polymorphic type:

newtype R = R { getR :: forall a. a -> IO String }

f :: IO R
f = return $ R (const getLine)

main :: IO ()
main = do
    g <- f
    getR g "String" >>= print
    getR g 5 >>= print

Upvotes: 6

Related Questions