GHC: Ticket #2222: Template Haskell: reify returns incorrect types when ommiting type signatures
http://ghc.haskell.org/trac/ghc/ticket/2222
<p>
Replicable with GHC versions 6.8.2 and 6.9 (20080219 snapshot).
</p>
<p>
Workaround: supply type signatures.
</p>
<p>
See the following examples:
</p>
<p>
<code>ReifyPlusTypeInferenceBugs.hs</code>
</p>
<pre class="wiki">{-# LANGUAGE TemplateHaskell #-}
module ReifyPlusTypeInferenceBugs where
import Language.Haskell.TH
-- First problem:
-- * reify doesn't return the expected type of names binded to
-- polymorphic expressions
-- a :: Num a => a
-- uncommenting the line above fixes the problem
a = 1
-- The following splice should print
-- "inside b: forall a_0 . GHC.Num.Num a_0 => a_0"
-- but instead, it merely prints a type variable "inside b: t_0"
b = $(do VarI _ t _ _ <- reify 'a
runIO $ putStrLn ("inside b: " ++ pprint t)
[| undefined |])
-- Second problem:
-- * reify doesn't return the expected type of names binded to
-- TH-spliced expressions if no explicit type signature
-- declaration is provided.
-- c :: Bool
-- uncommenting the line above fixes the problem
c = $([| True |])
-- this splice should print "inside d: GHC.Base.Bool"
-- but, again, it prints just a type variable: "inside d: t_0"
d = $(do VarI _ t _ _ <- reify 'c
runIO $ putStrLn ("inside d: " ++ pprint t)
[| undefined |] )
-- Strangely enough, reify works differently if called inside a declaration
-- splice. This time, the type returned is closer to be right
-- but unnecesary type variables are included:
-- "type of c: forall a_0 a_1 . GHC.Base.Bool"
$(do VarI _ t _ _ <- reify 'c
runIO $ putStrLn ("type of c: " ++ pprint t)
return [] )
-- Even more strange is the fact that the order of declaration of
-- splices seems to matter. Declaring the exact example again ....
-- e :: Bool
-- uncommenting the line above solves the problem
e = $([| True |])
-- this splice works as expected!!! ???
-- "inside f: GHC.Base.Bool"
f = $(do VarI _ t _ _ <- reify 'e
runIO $ putStrLn ("inside f: " ++ pprint t)
[| undefined |] )
-- Here, we still get unnecesary variables, but, for some reason,
-- _just_ one in this case:
-- "type of e: forall a_0 . GHC.Base.Bool"
$(do VarI _ t _ _ <- reify 'e
runIO $ putStrLn ("type of e: " ++ pprint t)
return [] )
</pre>en-usGHChttp://ghc.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://ghc.haskell.org/trac/ghc/ticket/2222
Trac 1.2.2simonpjWed, 16 Apr 2008 19:46:27 GMTdifficulty set
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:1
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:1
<ul>
<li><strong>difficulty</strong>
set to <em>Unknown</em>
</li>
</ul>
<p>
OK, so there are several things going on here.
</p>
<ol><li> For <code>a</code>, you are hitting the Monomorphism Restriction. Since <code>a</code> is monomorphic, it gets type <code>t0</code>, where <code>t0</code> is a unification variable. Right at the very end of the module we might see <code>(f a)</code> where <code>f :: Int -> Int</code>, and then (but only then) we'd discover that <code>t0</code> is really <code>Int</code>. The difficulty is that reification (for local variables) can ask for the type of a variable before all the evidence is in. A much more direct examples would be
<pre class="wiki"> \x. ... $( ...reify 'x'... ) ...
</pre></li></ol><p>
The type of <code>x</code> may not be determined by the time the splice runs. I can't see a way round this, except by making reification illegal for local variables, or perhaps for non-rigid ones, or something.
</p>
<ol start="2"><li> Although you wrote your definitions in order <code>b,c,d</code>, and they are not recursive, GHC is treating them as a mutually recursive group, and, as luck would have it, checks <code>d</code> first. So the reification inside <code>d</code> see's <code>c</code>'s type before <code>c</code>'s right hand side has been examined, and we are back in situation (1).
</li></ol><p>
Why are they treated as mutually recursive? Here's the comment from <code>RnSource</code>:
</p>
<pre class="wiki">Note [Splices]
~~~~~~~~~~~~~~
Consider
f = ...
h = ...$(thing "f")...
The splice can expand into literally anything, so when we do dependency
analysis we must assume that it might mention 'f'. So we simply treat
all locally-defined names as mentioned by any splice. This is terribly
brutal, but I don't see what else to do. For example, it'll mean
that every locally-defined thing will appear to be used, so no unused-binding
warnings. But if we miss the dependency, then we might typecheck
'h' before 'f', and that will crash the type checker because 'f' isn't
in scope.
Currently, I'm not treating a splice as also mentioning every import,
which is a bit inconsistent -- but there are a lot of them. We might
thereby get some bogus unused-import warnings, but we won't crash the
type checker. Not very satisfactory really.
</pre><p>
Remember that TH allows dynamic binding!
</p>
<p>
Again, I don't see a good way around this either.
</p>
<p>
You might say that you expect TH splices to be run top-to-bottom, but what if one at the bottom is used further up:
</p>
<pre class="wiki"> f = ...g...
...
h = $(bar 4)
g = $(foo 3)
</pre><p>
Now we have to run the splice for <code>g</code> before we can get a type for <code>g</code>; and we need a type for <code>g</code> before we can typecheck <code>f</code>.
</p>
<p>
Anyway I hope that explains some of what is going on. The real issues here are ones of design, rather than bugs of implementation. Good design ideas would be very welcome -- the TH design is clearly warty in places.
</p>
<p>
Simon
</p>
TicketiglooThu, 24 Apr 2008 17:17:21 GMTmilestone set
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:2
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:2
<ul>
<li><strong>milestone</strong>
set to <em>_|_</em>
</li>
</ul>
TicketsimonmarTue, 30 Sep 2008 15:37:31 GMTarchitecture changed
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:3
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:3
<ul>
<li><strong>architecture</strong>
changed from <em>Unknown</em> to <em>Unknown/Multiple</em>
</li>
</ul>
TicketsimonmarTue, 30 Sep 2008 15:54:55 GMTos changed
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:4
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:4
<ul>
<li><strong>os</strong>
changed from <em>Multiple</em> to <em>Unknown/Multiple</em>
</li>
</ul>
TicketmorabbinSun, 27 Jan 2013 17:20:44 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/2222
http://ghc.haskell.org/trac/ghc/ticket/2222
<ul>
<li><strong>attachment</strong>
set to <em>ReifyPlusTypeInferenceBugs.hs</em>
</li>
</ul>
<p>
Exhibits problems described in <a class="closed ticket" href="http://ghc.haskell.org/trac/ghc/ticket/2222" title="#2222: bug: Template Haskell: reify returns incorrect types when ommiting type ... (closed: fixed)">#2222</a>
</p>
TicketmorabbinSun, 27 Jan 2013 17:22:47 GMTfailure set
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:5
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:5
<ul>
<li><strong>failure</strong>
set to <em>None/Unknown</em>
</li>
</ul>
<p>
TH has moved on a little, and now all of the decl splices act as expected by OP:
</p>
<pre class="wiki">Orac:~/work/ghc $ ghci -fth ReifyPlusTypeInferenceBugs.hs
GHCi, version 7.6.1: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling ReifyPlusTypeInferenceBugs ( ReifyPlusTypeInferenceBugs.hs, interpreted )
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
inside d: t_0
inside b: a_0
type of c: GHC.Types.Bool
inside f: GHC.Types.Bool
type of e: GHC.Types.Bool
Ok, modules loaded: ReifyPlusTypeInferenceBugs.
</pre><p>
The output for <code>d</code> is precisely as OP reports, and output for <code>e</code> is the same modulo type variable change (from <code>t0</code> to <code>a0</code>).
</p>
TicketiglooFri, 01 Feb 2013 18:40:36 GMTstatus changed; testcase, resolution set
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:6
http://ghc.haskell.org/trac/ghc/ticket/2222#comment:6
<ul>
<li><strong>status</strong>
changed from <em>new</em> to <em>closed</em>
</li>
<li><strong>testcase</strong>
set to <em>T2222</em>
</li>
<li><strong>resolution</strong>
set to <em>fixed</em>
</li>
</ul>
<p>
I think it's now all as expected. I've added a test.
</p>
Ticket