GHC: Ticket #1482: unsafeCoerce# doesn't always fully coerce
http://ghc.haskell.org/trac/ghc/ticket/1482
<pre class="wiki">{-# OPTIONS_GHC -fglasgow-exts #-}
import GHC.Prim( unsafeCoerce# )
e1 = unsafeCoerce# (+)
e2 = unsafeCoerce# shows
</pre><p>
<tt>e1</tt> coerces fine, even with the <tt>Num</tt> constraint, but <tt>e2</tt> does not. The error for e2 is
</p>
<pre class="wiki"> Ambiguous type variable `a' in the constraint:
`Show a' arising from use of `shows' at T.lhs:7:20-24
Probable fix: add a type signature that fixes these type variable(s)
</pre><p>
Weirder still, ghci coerces <tt>shows</tt> fine:
</p>
<pre class="wiki">*Main> :t unsafeCoerce# shows
unsafeCoerce# shows :: forall b. b
</pre>en-usGHChttp://ghc.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://ghc.haskell.org/trac/ghc/ticket/1482
Trac 1.0.7sorearSun, 01 Jul 2007 21:55:15 GMT
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:1
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:1
<p>
Not a bug, but merely an extremely obscure feature of Haskell's type system.
</p>
<p>
Because unsafeCoerce# does not impose a specific type upon the use of (+) or shows, the polymorphic type variable becomes ambiguous. However, since both (+) and shows need to know the actual type of use (due to the implicit dictionary argument), this variable needs to be resolved somehow, or an error produced.
</p>
<p>
Section 4.3.4 of the Haskell 98 Language and Libraries Report ( <a class="ext-link" href="http://haskell.org/onlinereport/decls.html#sect4.3.4"><span class="icon"></span>http://haskell.org/onlinereport/decls.html#sect4.3.4</a> ) specifies that ambiguous type variables are resolved using a default list, but only if (a) no non-standard classes are used and (b) at least one numeric class is used. The idea behind this was to make numeric code easier, as even 'show 2' would otherwise result in a type error. Thus, in Haskell 98 (with a unsafeCoerce primitive), the first example is legal and the second is not (since defaulting does not apply unless numeric classes are involved.) However, even the Haskell98 defaulting rules proved somewhat too onerous for REPL-type situations, so GHC provides a -fextended-default-rules flag ( <a class="ext-link" href="http://haskell.org/ghc/dist/current/docs/users_guide/interactive-evaluation.html#extended-default-rules"><span class="icon"></span>http://haskell.org/ghc/dist/current/docs/users_guide/interactive-evaluation.html#extended-default-rules</a> ) flag to allow more cases, including your second one.
</p>
<p>
Hopefully this clears things up.
</p>
<p>
Stefan
</p>
Ticketyeoh@…Sun, 01 Jul 2007 22:32:38 GMT
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:2
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:2
<p>
I was going to add the comment below, but you've beat me to it.
</p>
<p>
Adding <tt>-fextended-default-rules</tt> definitely makes it all work, in ways I still don't fully understand. There is no ambiguity in the <tt>forall a b. a -> b</tt> explicit type signature, but the error message suggests that the typechecker refuses to surrender the <tt>Show a</tt> constraint. Am I mistaken in this interpretation?
</p>
<p>
If this is expected behavior, perhaps an error message suggesting <tt>-fextended-default-rules</tt> may assist the next unwary bystander.
</p>
<hr />
<p>
Here's what I mean by not fully coercing. This typechecks:
</p>
<pre class="wiki">{-# OPTIONS_GHC -fglasgow-exts -fno-monomorphism-restriction #-}
import GHC.Prim( unsafeCoerce# )
e2 = unsafeCoerce# (shows::a -> String -> String) ::
forall a b. Show a => a -> b
</pre><p>
However, the <tt>Show a</tt> constraint must be carried around all the time which of course, severely hampers the extent of the coercion. Omitting the constraint gives me
</p>
<pre class="wiki"> No instance for (Show a)
arising from use of `shows' at T.lhs:6:20-24
Possible fix: add (Show a) to the expected type of an expression
In the first argument of `unsafeCoerce#', namely
`(shows :: a -> String -> String)'
In the expression:
unsafeCoerce# (shows :: a -> String -> String) ::
forall a b. a -> b
In the definition of `e2':
e2 = unsafeCoerce# (shows :: a -> String -> String) ::
forall a b. a -> b
</pre>
TicketIsaac DupreeMon, 02 Jul 2007 20:18:06 GMT
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:3
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:3
<p>
LOL, I accidentally left out the ":t" in GHCi, and got (same result on x86 6.6.1)
</p>
<pre class="wiki">> unsafeCoerce# shows
<interactive>: internal error: PAP object entered!
(GHC version 6.6.1 for powerpc_unknown_linux)
Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug
Aborted (core dumped)
</pre><p>
What kind of showing is GHC trying to do on an object of type (forall b. b) anyway?
</p>
<p>
Passing -fno-extended-default-rules didn't work for me to disable that in ghci, so I went to a .hs file:
</p>
<p>
No, (shows::a -> String -> String) doesn't typecheck. (forall a. a -> String -> String) isn't a type of <tt>shows</tt>, only (forall a. Show a => a -> String -> String) is.
</p>
<pre class="wiki">f :: forall a. a -> String -> String
f = undefined
e1 = ( unsafeCoerce# ) f
</pre><p>
typechecks. Type variables that could be anything at all, don't need to be instantiated.
</p>
<pre class="wiki">e2 = (unsafeCoerce# :: (forall a. Show a => a -> String -> String) -> b) shows
</pre><p>
typechecks. GHC can't infer a rank-2 type for unsafeCoerce, but you can give it one (<tt>forall b. (forall a. Show a => a -> String -> String) -> b</tt> is clearly a more-specific version of unsafeCoerce's type <tt>forall a b. a -> b</tt>). This allows the <em>polymorphic</em> shows to be passed to unsafeCoerce, so that <tt>shows</tt> object can then be coerced as normal. (Rank-two types with typeclasses aren't nice, you always have to specify a new type signature when the set of type-classes is different...)
</p>
<p>
Neither -fno-monomorphism-restriction nor defaulting are needed.
</p>
<p>
Note that there is a difference between
<tt>(unsafeCoerce# :: (Integer -> String -> String) -> b) shows</tt>
(or whatever the type variable would default to, if not Integer)
and
<tt>(unsafeCoerce# :: (forall a. Show a => a -> String -> String) -> b) shows</tt>
.
Their representations are not the same; the second one can only be coerced back to the fully polymorphic version, and the first one can only be coerced back to the Integer version (well, probably if you wanted to pass _|_ for that parameter then you could also coerce the second one to have that parameter's type be anything you wanted, including polymorphic, as long as it wasn't qualified with a type-class constraint).
</p>
TicketIsaac DupreeMon, 02 Jul 2007 20:41:59 GMT
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:4
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:4
<p>
oops, near the end, "probably...you could also coerce the second one" should be "the first one", rather (that is, the non-rank-2 version)
</p>
TicketiglooSat, 18 Aug 2007 00:36:41 GMTstatus changed; resolution set
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:5
http://ghc.haskell.org/trac/ghc/ticket/1482#comment:5
<ul>
<li><strong>status</strong>
changed from <em>new</em> to <em>closed</em>
</li>
<li><strong>resolution</strong>
set to <em>invalid</em>
</li>
</ul>
<p>
Defaulting only happens in H98 if you have a Num constraint. If you use <tt>-fextended-default-rules</tt> then the original example works:
</p>
<pre class="wiki">{-# OPTIONS_GHC -fglasgow-exts -fextended-default-rules #-}
import GHC.Prim( unsafeCoerce# )
e1 = unsafeCoerce# (+)
e2 = unsafeCoerce# shows
</pre><p>
Alternatively (and the preferred option), you can give shows an explicit type, so no defaulting needs to happen:
</p>
<pre class="wiki">{-# OPTIONS_GHC -fglasgow-exts #-}
import GHC.Prim( unsafeCoerce# )
e1 = unsafeCoerce# (+)
e2 = unsafeCoerce# (shows :: () -> ShowS)
</pre>
Ticket