[GHC] #16396: TH doesn't preserve `forall`

GHC ghc-devs at haskell.org
Wed Mar 6 00:10:20 UTC 2019


#16396: TH doesn't preserve `forall`
-------------------------------------+-------------------------------------
           Reporter:  goldfire       |             Owner:  (none)
               Type:  bug            |            Status:  new
           Priority:  normal         |         Milestone:
          Component:  Template       |           Version:  8.6.3
  Haskell                            |
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  None/Unknown
  Unknown/Multiple                   |
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 I think there are several bugs in the way TH currently deals with type
 variable quantification.

 **Background**

 GHC uses the "forall-or-nothing" rule. This means that, if there is a top-
 level `forall` in a type signature, that `forall` must bind ''all''
 variables being brought into scope in that signature. Examples:

 {{{#!hs
 f1 :: a -> a
 f1 x = x    -- OK, no forall

 f2 :: forall . a -> a
 f2 x = x    -- rejected

 f3 :: forall a. a -> a
 f3 x = x    -- OK, forall binds all variables

 f4 :: forall a. a -> a
 f4 x = helper x
   where helper :: a -> a        -- OK; this uses a already in scope
         helper _ = x

 f5 :: forall a. a -> a
 f5 x = helper x
   where helper :: forall . a -> a   -- OK; a is already in scope
         helper _ = x

 f6 :: forall a. a -> a
 f6 x = helper x
   where helper :: forall a. a -> a   -- this is a new variable `a`
         helper _ = x     -- rejected; the two `a`s are different
 }}}

 Upshot: the existence of `forall` matters.

 **Strangeness 1**

 {{{#!hs
 foo1 :: $([t| a -> a |])
 foo1 x = x
 }}}

 is rejected, because `a` is not in scope. This is incongruent with the
 treatment of `f1`. One could argue, though, that all type variables used
 in quotes should be in scope. I would disagree with such an argument
 (that's not what we do these days in terms), but it's not silly.

 **Strangeness 2**

 {{{#!hs
 foo2 :: $([t| $(varT (mkName "a")) -> $(varT (mkName "a")) |])
 foo2 x = x
 }}}

 is rejected because `a` is not in scope. This behavior seems just wrong.

 **Strangeness 3**

 {{{#!hs
 foo3 :: $([t| forall . $(varT (mkName "a")) -> $(varT (mkName "a")) |])
 foo3 x = x
 }}}

 is rejected in the same way as `foo2`. While this ''should'' be rejected
 (it's `f2`), `-ddump-splices` says that the splice evaluates to `a -> a`;
 note: no `forall`. So it's rejected under false pretenses.

 **Strangeness 4**

 {{{#!hs
 foo4 :: $([t| forall a . $(varT (mkName "a")) -> $(varT (mkName "a")) |])
 foo4 x = x
 }}}

 This one is accepted (as it should be), but it produces a warning!

 {{{
 Scratch.hs:51:21: warning: [-Wunused-foralls]
     Unused quantified type variable ‘a’
     In the type ‘forall a.
                  $(varT (mkName "a")) -> $(varT (mkName "a"))’
    |
 51 | foo :: $([t| forall a . $(varT (mkName "a")) -> $(varT (mkName "a"))
 |])
    |
 }}}

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/16396>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list