Type checker's expected and inferred types

Daniel Fischer daniel.is.fischer at web.de
Fri Oct 23 22:50:11 EDT 2009


Am Samstag 24 Oktober 2009 03:12:14 schrieb C Rodrigues:
> I came across a type error that misled me for quite a while, because the
> expected and inferred types were backwards (from my point of view).  A
> simplified example is below.  Can someone explain how GHC's type checker
> creates the error message? In this example, fun1 and fun2 are basically the
> same.  The type error is because they try to run an IO () together with a
> Maybe ().
>
> 

import Control.Monad
foo :: Maybe ()
foo = return ()

bar :: IO ()
bar = return ()

fun1 = let fooThen m = foo >> m
         in fooThen (bar >> undefined)


fun2 = let fooThen m = foo >> m
         in fooThen (do {bar; undefined})

>
> With ghc 6.10.4, both functions attribute the error message to `bar'.
> However, the expected and inferred monads are swapped.fun1 produces the
> error message:

> Couldn't match expected type `Maybe a' against inferred type `IO ()'
> In the first argument of `(>>=)', namely `bar'

> fun2 produces the error message:

> Couldn't match expected type `IO ()' against inferred type `Maybe ()'
> In a stmt of a 'do' expression: bar 


> It's confusing because 'bar'
> is inferred to have type Maybe (), even though it's explicitly declared to
> be an IO ().

I don't know the intricate details, but the order in which type inference/type checking 
proceeds has something to do with it.

In fun1, apparently first the type of fooThen is inferred to be `Maybe a -> Maybe a'.
Then, in the body of the let-expression, fooThen *expects* a `Maybe a'.
Thus the (>>) in fooThen's argument is inferred to have the type 
`Maybe a -> Maybe b -> Maybe b'.
Hence the first argument of (>>) [or (>>=), apparently it has been expanded to that]
is *expected* to have type `Maybe a'.
But from bar's definition, its type is *inferred* to be `IO ()'.

Perfectly clear and transparent (not really). Had the type of fooThen's argument been 
inferred before fooThen's type, it would've said that it expected fooThen to have type `IO 
a -> b', but inferred it to have type `Maybe a -> Maybe a'.

The error for fun2 is baffling. I can't explain how ghci comes to *expect* bar to have 
type `IO ()'.

Also intriguing is that if you swap bar and undefined in the do-expression, you get the 
error message you'd expect:

    Couldn't match expected type `Maybe b'
           against inferred type `IO ()'  
    In the expression: bar                
    In the first argument of `fooThen', namely
        `(do undefined                        
             bar)'                            
    In the expression:                        
        fooThen                               
          (do undefined                       
              bar)

But if you sandwich bar between two actions which may have type `Maybe a', it's back to 
expecting `IO ()':

    Couldn't match expected type `IO ()'
           against inferred type `Maybe ()'
    In a stmt of a 'do' expression: bar
    In the first argument of `fooThen', namely
        `(do Just 1
             bar
             Nothing)'
    In the expression:
        fooThen
          (do Just 1
              bar
              Nothing)

Seems typechecking do-expressions has some strange corners.


More information about the Glasgow-haskell-users mailing list