Opened 8 years ago

Closed 8 years ago

Last modified 7 years ago

#4125 closed bug (invalid)

Template haskell rejects duplicate instances before they're spliced

Reported by: lilac Owned by:
Priority: normal Milestone: 7.4.1
Component: Template Haskell Version: 6.12.1
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: GHC rejects valid program Test Case:
Blocked By: #4230 Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

Suppose I want to see (from within ghci) how to write an instance declaration in the TH AST. I try:

Prelude> runQ $ [d| instance Monad Maybe where |]                                                                                                                                                                                                   
                                                                                                                                                                                                                                             
<interactive>:1:13:                                                                                                                                                                                                                          
    Duplicate instance declarations:                                                                                                                                                                                                         
      instance Monad Maybe -- Defined at <interactive>:1:13-23                                                                                                                                                                               
      instance Monad Maybe -- Defined in Data.Maybe                                                                                                                                                                                          
    In the Template Haskell quotation                                                                                                                                                                                                        
      [d|                                                                                                                                                                                                                                    
          instance Monad Maybe where |]                                                                                                                                                                                                      
    In the expression:                                                                                                                                                                                                                       
        [d|                                                                                                                                                                                                                                  
            instance Monad Maybe where |]                                                                                                                                                                                                    
    In the definition of `it':                                                                                                                                                                                                               
        it = [d|                                                                                                                                                                                                                             
                 instance Monad Maybe where |]

This seems ridiculous; I'm not trying to create an instance, just to create a TH AST fragment.

Change History (6)

comment:1 Changed 8 years ago by simonpj

I can see why you think it's ridiculous, but there an underlying reason. Remember that TH quotations are typechecked. So if you write [| map f [a,b] |], TH checks that the argument of map is compatible with map's type. To do that, it uses the type environment from outside the quote, which binds map.

Your example contains an instance declaration. If you saw [| return True :: T Bool |] you'd probably expect that to typecheck if (and only if) T is an instance of Monad. Again, the instance environment comes from outside the quote.

In this case it's a declaration splice but the story is the same: the declarations are typechecked in the type/instance environment from outside the quote; and that's why you get the complaint about a duplicate.

So I'm going to flag this as "invalid", not because it's silly but because GHC is behaving according to specification at the moment. I suppose one could imagine some kind of feature that would support what you want, but I'm not sure what the design would be. If you feel like producing a design, by all means open a new feature request.

Simon

comment:2 Changed 8 years ago by simonpj

Resolution: invalid
Status: newclosed

comment:3 Changed 7 years ago by lilac

Your example contains an instance declaration. If you saw [| return True :: T Bool |] you'd probably expect that to typecheck if (and only if) T is an instance of Monad.

Actually, no, and I think that's the essence of the problem. I think the feature I want is to turn off all typechecking within TH quotations (or, more accurately, to defer all such checking until the quotation is actually spliced). I have a few arguments in favour of this change:

Firstly, suppose today I write:

module Main where
data T a = T
[d|instance Monad T where return _ = T; T >>= f = T|]
[d|foo = return True :: T Bool|]

If I now refactor this:

module Main where
import Control.Monad
data T a = T
liftM2 (++) [d|instance Monad T where return _ = T; T >>= f = T|] [d|foo = return True :: T Bool|]

... it would fail to compile. Result: TH code is not composable.

As it happens, I've committed some intellectual fraud above: [| return True :: T Bool |] is accepted by GHC 6.12.1 whether or not a Monad T instance is in scope. And that's my second argument: as it stands, it is very unclear what code will be accepted and what code will not; the only way to know seems to be to try it on the versions of GHC you care about. If I used the second code fragment above, would I run the risk that it'd be broken in later versions of GHC with "better" typechecking?

My third argument is: the purpose of static types is to reject incorrect programs (while minimizing the number of correct programs rejected). It seems to me that typechecking TH prior to the splice point can only reject correct programs (any incorrect program would be rejected anyway by post-splice typechecking).

Is the above anywhere near convincing enough for it to be worth opening a feature request?

comment:4 Changed 7 years ago by simonpj

See #4230 for a more global discussion

Simon

comment:5 Changed 7 years ago by igloo

Blocked By: 4230 added

comment:6 Changed 7 years ago by igloo

Milestone: 6.16.1
Note: See TracTickets for help on using tickets.