[GHC] #10271: Typed Template Haskell splice difficulty when resolving overloading

GHC ghc-devs at haskell.org
Wed Apr 8 22:31:30 UTC 2015


#10271: Typed Template Haskell splice difficulty when resolving overloading
-------------------------------------+-------------------------------------
              Reporter:  simonpj     |             Owner:
                  Type:  bug         |            Status:  new
              Priority:  normal      |         Milestone:
             Component:  Compiler    |           Version:  7.10.1
              Keywords:              |  Operating System:  Unknown/Multiple
          Architecture:              |   Type of failure:  None/Unknown
  Unknown/Multiple                   |        Blocked By:
             Test Case:              |   Related Tickets:
              Blocking:              |
Differential Revisions:              |
-------------------------------------+-------------------------------------
 J Garrett Morris describes the following surprising
 behaviour for typed Template Haskell
 {{{
 {-# LANGUAGE TemplateHaskell, FlexibleInstances #-}
 module PrintfLib where
     import Language.Haskell.TH

     class Printf t where
       printf :: String -> Q (TExp String) -> Q (TExp t)

     instance Printf [Char] where
       tf s t | '%' `notElem` s = [|| $$t ++ s ||]
              | otherwise       = fail ("Unexpected format %"
                                        ++ [c])
       where (_, _:c:_) = break ('%' ==) s

     instance Printf t => Printf (Char -> t) where
       printf s t
         | c /= 'c' = fail ("Unexpected format %" ++ [c] ++
                            " for character")
         | otherwise = [|| \c -> $$(printf s''
                                      [|| $$t ++ s' ++ [c] ||])
                        ||]
           where (s', '%':c:s'') = break ('%' ==) s

 -------------------------
 {-# LANGUAGE TemplateHaskell #-}
 module Printf where
     import PrintfLib

     f :: Char -> String
     f = $$(printf "foo %c" [||""||])

     h :: Char -> String
     h y = $$(printf "foo %c" [||""||]) y
 }}}
 Oddly, `f` typechecks but `h` does not, even though `h` is just an eta-
 expanded version of `f`:
 {{{
 Printf.hs:9:10:
     No instance for (Printf t0) arising from a use of ‘printf’
     The type variable ‘t0’ is ambiguous
     Note: there are several potential instances:
       instance Printf t => Printf (Char -> t) -- Defined in ‘PrintfLib’
       instance Printf [Char] -- Defined in ‘PrintfLib’
     In the expression: printf "foo %c" [|| "" ||]
 }}}
 What is going on?  Here's the deal

  * To run the splice, GHC must solve any constraints that arise form the
 expression `(printf "foo %c" ...)`.
  * Since `printf` is overloaded, and overloaded on its result type, the
 type needed by the context of the splice is what determines which instance
 of `Printf` is needed.
  * In `f` the context needs `Char -> String`, and so the call to `printf`
 must have type `TExpr (Char -> String)`, so we get the constraint `Printf
 (Char -> String)` which we can solve.
  * But in `h` the rule for application tries to ''infer'' a type for the
 splice.  So the context for the call just says `TExp t0` for some
 unification variable `t0`; and that leads to the insoluble constraint.

 You may say that GHC should be cleverer, and push that type signature
 information into the application. And perhaps it should.  But you can
 never win. For example:
 {{{
   hard x = [ $$(printf "gluk" [|| "" ||]), undefined :: Char -> String ]
 }}}
 Here the RHS of `hard` is a 2-element list. Since all elements of a list
 have the same type,
 the splice must have the same type as the second element of the list,
 namely `Char->String`.  But seeing that would mean that we'd have to
 typecheck right-to-left.  In general GHC tries very very hard NOT to
 depend on traveral order.  There is no way in general to ensure that we
 have all the information now that constraint solving may ultimately
 produce.

 I'm not sure what to do about this.
  * It seldom matters, because resolving the overloading on the splice
 seldom depends on the result type.
  * When it does matter, you can fix it by giving a type signature to the
 splice itself.
 But it seems unsatisfactory.  Ideas welcome.

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


More information about the ghc-tickets mailing list