Ticket #3613 (closed bug: fixed)

Opened 4 years ago

Last modified 3 years ago

Better error messages for do-notation

Reported by: simonpj Owned by:
Priority: normal Milestone: 7.0.1
Component: Compiler (Type checker) Version: 6.10.4
Keywords: Cc: red5_2@…, michal.terepeta@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Incorrect warning at compile-time Difficulty: Unknown
Test Case: typecheck/should_fail/T3613 Blocked By:
Blocking: Related Tickets:

Description

C Rodrigues [red5_2@…] writes: 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 ().

Change History

Changed 4 years ago by simonpj

I agree this is confusing. I had a look at the code. When typechecking x <- rhs, GHC does this (TcMatches.tcDoStmt)

  • First infers the type of rhs
  • Then feed that into a typecheck of the bind operator >>=

Instead, the programmer expects the reverse, so that information about which monad is involved gets fed into the argument.

I don't expect anyone else to understand this note; it's an aide-memoire for me to remind me to fix this.

Simon

Changed 4 years ago by simonpj

  • milestone set to 6.14 branch

I've improved matters as a side consequence of

Wed Oct 28 06:35:54 PDT 2009  simonpj@microsoft.com
  * Add 'rec' to stmts in a 'do', and deprecate 'mdo'
  
  The change is this (see Trac #2798).  
   ...

However the interaction of GADTs and impredicative polymorphism defeated me, so I only dealt with (>>) and not (>>=).

When the long-heralded re-engineering of type inference for GADTs takes place, I'll come back to this.

Simon

Changed 3 years ago by igloo

  • milestone changed from 6.14 branch to 6.14.1

Changed 3 years ago by michalt

  • cc michal.terepeta@… added
  • failure set to Incorrect warning at compile-time

This seems to work fine with HEAD (i.e. even with (>>=)). So adding the following to the original examples:

> fun1' = let fooThen m = foo>> m
>         in fooThen (bar>>= \x -> undefined)
>
> fun2' = let fooThen m = foo>> m
>         in fooThen (do {x <- bar; undefined})

I get:

  • for fun1:
          Couldn't match expected type `Maybe a' with actual type `IO ()'
          In the first argument of `(>>)', namely `bar'
    
  • for fun2:
          Couldn't match expected type `Maybe a' with actual type `IO ()'
          In a stmt of a 'do' expression: bar
    
  • for fun1':
          Couldn't match expected type `Maybe a' with actual type `IO ()'
          In the first argument of `(>>=)', namely `bar'
    
  • and finally for fun2':
          Couldn't match expected type `Maybe a' with actual type `IO ()'
          In a stmt of a 'do' expression: x <- bar
    

Which seems ok to me. So unless I'm missing something the ticket can be closed as fixed..?

Just for the record:

> ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.1.20101008

Changed 3 years ago by simonpj

  • status changed from new to closed
  • testcase set to typecheck/should_fail/T3613
  • resolution set to fixed

Ah yes, it does seemt to be fixed. Moreover, we already have a regression test to check it stays fixed (I've added it to the ticket description, but it was already in the testsuite). So yes let's close it.

Thanks for pointing this out; very helpful.

Simon

Note: See TracTickets for help on using tickets.