GHC: Ticket #9223: Type equality makes type variable untouchable
http://trac.haskell.org/trac/ghc/ticket/9223
<p>
This doesn't look right:
</p>
<pre class="wiki">{-# LANGUAGE RankNTypes, TypeFamilies #-}
type family TF (m :: * -> *)
run1 :: (forall m . Monad m => m a) -> a
run1 = undefined
run2 :: (forall m . (Monad m, TF m ~ ()) => m a) -> a
run2 = undefined
-- this works
x1 = run1 (return ())
-- this fails
x2 = run2 (return ())
{-
Couldn't match expected type ‘a’ with actual type ‘()’
‘a’ is untouchable
inside the constraints (Monad m, TF m ~ ())
bound by a type expected by the context:
(Monad m, TF m ~ ()) => m a
at untouchable.hs:15:6-21
‘a’ is a rigid type variable bound by
the inferred type of x2 :: a at untouchable.hs:15:1
Relevant bindings include x2 :: a (bound at untouchable.hs:15:1)
In the first argument of ‘return’, namely ‘()’
In the first argument of ‘run2’, namely ‘(return ())’
-}
</pre><p>
I would expect x2 to type check just like x1 does.
</p>
en-usGHChttp://trac.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://trac.haskell.org/trac/ghc/ticket/9223
Trac 1.2.2goldfireMon, 23 Jun 2014 02:35:39 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:1
http://trac.haskell.org/trac/ghc/ticket/9223#comment:1
<p>
I'm a little confused about what's going on about the core complaint, but, tangentially, it seems to me that calling <code>a</code> a "rigid type variable" is wrong here. Isn't <code>a</code> a metavariable, especially if it's untouchable?
</p>
<p>
I should say that blindly following the rules from OutsideIn about untouchable variables does lead me to a place of unifying <code>m a</code> with <code>m ()</code> where <code>a</code> is untouchable. But, something is still fishy about it that I can't quite pin down. In other cases, when something is untouchable, I can construct a way in which the type in question is not a principal type... and I can't quite do this here, hence my confusion.
</p>
TicketsimonpjMon, 23 Jun 2014 07:53:54 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:2
http://trac.haskell.org/trac/ghc/ticket/9223#comment:2
<p>
GHC is being schizophrenic about an interesting design choice here. The issue is this.
</p>
<p>
When typechecking <code>x2</code>'s RHS, we assume that the RHS has type <code>alpha</code>, for some fresh unification variable <code>alpha</code>, and generate constraints from the RHS, giving the constraint:
</p>
<pre class="wiki">forall m. (Monad m, TF m ~ ()) -- Comes from run2's type
=> (m alpha ~ m (), -- Matching result of (return ()) with expected type (m alpha)
Monad m) -- Call of return
</pre><p>
That leads to the constraint <code>(alpha ~ ())</code>, but underneath an implication constraint that has an equality (like a GADT) in it. So OutsideIn refrains from unifying <code>alpha</code>.
</p>
<p>
Then GHC tries to infer a type for <code>x2</code> anyway, getting (essentially)
</p>
<pre class="wiki">x2 :: forall a. (a ~ ()) => a
</pre><p>
where we have decided to quantify over <code>alpha</code> to <code>a</code>. Finally we end up reporting the insoluble constraint <code>(a ~ alpha)</code>.
</p>
<p>
So the question is: what error message would you like to see? This "untouchable" stuff is a bit disconcerting, so we could say this:
</p>
<pre class="wiki"> Couldn't match expected type ‘a’ with actual type ‘()’
‘a’ is a rigid type variable bound by
the inferred type of x2 :: forall a. a at untouchable.hs:15:1
Relevant bindings include x2 :: a (bound at untouchable.hs:15:1)
In the first argument of ‘return’, namely ‘()’
In the first argument of ‘run2’, namely ‘(return ())’
</pre><p>
Here you get to see the inferred type of <code>x2</code>. (Side note: actually GHC currently prints <code>the inferred type of x2 :: a</code>, suppressing the <code>forall</code>, but in this context I suspect we should print the <code>forall</code> regardless of <code>-fprint-explicit-foralls</code>. I'll make that change anyway unless anyone yells.)
</p>
<p>
A merit of this error message is that if you, the programmer, give <code>x2</code> that type signature <code>x2 :: forall a. a</code>, then indeed that is very much the message that you'll get. But I suppose it leaves open the question of <strong>why</strong> GHC inferred that type for <code>x2</code>.
</p>
<p>
Alternatively we could give you
</p>
<pre class="wiki"> Couldn't match expected type ‘a’ with actual type ‘()’
‘a’ is untouchable
inside the constraints (Monad m, TF m ~ ())
bound by a type expected by the context:
(Monad m, TF m ~ ()) => m a
at untouchable.hs:15:6-21
Relevant bindings include x2 :: a (bound at untouchable.hs:15:1)
In the first argument of ‘return’, namely ‘()’
In the first argument of ‘run2’, namely ‘(return ())’
</pre><p>
which perhaps exposes a bit more of the mechanism of OutsideIn, and has the merit of displayin the constraint(s) that make it untouchable.
</p>
<p>
Which would you prefer? Currently GHC displays both, which I agree is confusing.
</p>
<p>
Simon
</p>
TicketgoldfireTue, 24 Jun 2014 18:22:57 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:3
http://trac.haskell.org/trac/ghc/ticket/9223#comment:3
<p>
I may have said this elsewhere, but if we keep the "untouchable" stuff in the error message, it would be great to include a link to more information. Untouchable variables are really subtle, and an understanding of them is necessary for a programmer to make progress here. Section 5.2 of the OutsideIn paper is a great starting place for what should be at the other end of the link.
</p>
<p>
Personally, I favor the second message above -- it has more information a programmer might use to fix the problem.
</p>
TickettvynrFri, 05 Dec 2014 19:41:38 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:4
http://trac.haskell.org/trac/ghc/ticket/9223#comment:4
<p>
I just ran into something that seems at least related. I'm trying to use Happy in tandem with statically typing the payload I get from different functions (so I don't have to do the thing the GHC parser does with explicit calls to unwrapping functions). Eventually, I got the essence of the problem down to this MWE:
</p>
<pre class="wiki">{-# LANGUAGE GADTs, ExistentialQuantification #-}
module Main where
data Token t = forall a. Token (SomeToken t a)
data SomeToken t a = SomeToken (t a) a
data TokenType a where
TokLitInt :: TokenType Integer
TokLitPlus :: TokenType ()
-- Without the type signature, GHC 7.8.3 can't infer the type of this function.
--foo :: Token TokenType -> Integer
foo (Token (SomeToken TokLitInt x)) = x + 1
foo _ = 0
main :: IO ()
main = undefined
</pre><p>
The above code typechecks in GHC 7.6.3 with and without the type signature. In GHC 7.8.3, it fails to typecheck (unless the type signature is present) with the following error message:
</p>
<pre class="wiki"> Couldn't match type ‘a’ with ‘Integer’
‘a’ is untouchable
inside the constraints (a1 ~ Integer)
bound by a pattern with constructor
TokLitInt :: TokenType Integer,
in an equation for ‘foo’
at Test.hs:15:23-31
‘a’ is a rigid type variable bound by
the inferred type of foo :: Num a => Token TokenType -> a
at Test.hs:15:1
Expected type: a
Actual type: a1
Relevant bindings include
foo :: Token TokenType -> a (bound at Test.hs:15:1)
In the expression: x + 1
In an equation for ‘foo’:
foo (Token (SomeToken TokLitInt x)) = x + 1
</pre><p>
This is unfortunate for me because Happy doesn't generate type signatures but does, in the approach I'm using, wind up generating code that behaves equivalently. Putting a type signature on the use of "x" doesn't help, either, though I'm guessing that this is unsurprising to those who understand the relevant paper better. :)
</p>
<p>
If you'll forgive my naiveté, it seems pretty peculiar from the perspective of an amateur Haskell programmer that this type isn't inferred; I'd think that the type variable from the two arguments of SomeToken would be unified by the fact that they were introduced by the same pattern match. But the error message indicates two variables, "a" and "a1", and doesn't really seem to relate them. Am I missing something?
</p>
<p>
That said, I need to read the paper mentioned in this thread rather thoroughly now and I understand if it sounds like I'm asking for a pony. :)
</p>
TicketgoldfireFri, 05 Dec 2014 20:13:22 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:5
http://trac.haskell.org/trac/ghc/ticket/9223#comment:5
<p>
Interesting example! I'm not convinced this is related to the original ticket, but it is curious. I do see why OutsideIn refuses to type-check this, in the presence of the GADT pattern-match on <code>TokIntLit</code>. But what's interesting to me is that I can't come up with a type other than <code>Token TokenType -> Integer</code> for <code>foo</code>. As I note above, usually when we get untouchable variable errors, there is an alternate type that could be assigned... but here, that doesn't seem to be the case, because the GADT pattern-match is happening over an existentially-bound variable.
</p>
<p>
I don't know what the fix is here, but it's an interesting case.
</p>
TickettvynrFri, 05 Dec 2014 20:22:44 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:6
http://trac.haskell.org/trac/ghc/ticket/9223#comment:6
<p>
I'm glad the example is at least somewhat useful. :) I'm unfamiliar enough with the mechanics involved that I don't know if this is related. Should I open another ticket instead or just leave this stuff here?
</p>
<p>
The thing that seems especially peculiar to me is that, in the example above, GHC 7.6.3 can infer the type just fine. This suggests that the GHC 7.8.3 inference algorithm has been made more conservative in some way. I'm guessing that this difference is related to fixing a bug of some kind but, as you say, this case only seems to have the one type which could be inferred.
</p>
<p>
In the meanwhile, I suppose we'll just use the workaround of an explicit unwrapper function if we need to move to GHC 7.8. Thanks!
</p>
TicketgoldfireFri, 05 Dec 2014 20:27:19 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:7
http://trac.haskell.org/trac/ghc/ticket/9223#comment:7
<p>
My guess is that this <em>is</em> separate from the first post, but, in both cases, there is an untouchable type variable where there is a principal type. I'd wait for Simon's opinion before splitting this off.
</p>
TicketsimonpjSat, 06 Dec 2014 00:08:30 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:8
http://trac.haskell.org/trac/ghc/ticket/9223#comment:8
<p>
Consider
</p>
<pre class="wiki">data T a where
T1 :: T Int
T2 :: T Bool
T3 :: T a
f T2 = True
g T1 = True
</pre><p>
Can we infer a type for <code>f</code>? Well we know it must be something like
</p>
<pre class="wiki">f :: T a -> ??
</pre><p>
just from the lambda and the pattern match. What is the <code>??</code>? Call it <code>beta</code>, a unification variable. The pattern match gives us a "given" constraint <code>a~Bool</code>, and the RHS has type <code>Bool</code>, so the wanted" constraint is <code>beta~Bool</code>. So we could unify <code>beta with </code>Bool<code> or with </code>a`; both would work. Since neither is more general than the other, we reject the program.
</p>
<p>
What about <code>g</code>? Here we have "given" <code>a~Int</code>, but the same wanted constraint is <code>beta~Bool</code> is generated. Now there is only one solution, namely <code>beta:=Bool</code>, because the given <code>a~Int</code> can't help us with booleans.
</p>
<p>
But you can see how delicate this is! <code>f</code> has no principal type, while <code>g</code> does, but the difference is very slight. It may be obvious to a naive programmer, but it certainly isn't obvious to GHC.
</p>
<p>
GHC conservatively rejects both. Your program is another variation with an existentially bound type instead of <code>Int</code> or <code>Bool</code>. 7.6.3 simply had a bug.
</p>
<p>
I have no idea how to improve on this. But that's not to say it could not be improved.
</p>
<p>
Simon
</p>
TickettvynrSat, 06 Dec 2014 00:24:14 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:9
http://trac.haskell.org/trac/ghc/ticket/9223#comment:9
<p>
Ah ha! This makes perfect sense; thank you for the excellent example and explanation. In particular, this points out that whether a principled type exists is dependent upon some pretty incidental factors, like whether the output type and the input type happen to have anything in common. I can see why GHC conservatively rejects both of these, since getting it to work would result in a somewhat fragile system.
</p>
<p>
I had previously supposed that the problem was somehow related to the fact that I had two data -- the <code>TokenType</code> and the payload -- and that the <code>a</code> in the first parameter of <code>SomeToken</code> (<code>t a</code>) was not being unified with the <code>a</code> in the second parameter (<code>a</code>) for the purposes of inference. I think I had gotten than impression by the names <code>a</code> and <code>a1</code> without realizing that one of them arose merely due to the use of <code>+</code> in the body.
</p>
<p>
Thanks again!
</p>
TicketgoldfireSat, 06 Dec 2014 21:16:57 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:10
http://trac.haskell.org/trac/ghc/ticket/9223#comment:10
<p>
Replying to <a class="ticket" href="http://trac.haskell.org/trac/ghc/ticket/9223#comment:8" title="Comment 8">simonpj</a>:
</p>
<blockquote class="citation">
<p>
What about <code>g</code>? Here we have "given" <code>a~Int</code>, but the same wanted constraint is <code>beta~Bool</code> is generated. Now there is only one solution, namely <code>beta:=Bool</code>, because the given <code>a~Int</code> can't help us with booleans.
</p>
</blockquote>
<p>
I would argue that <code>g</code> does <em>not</em> have a principal type. It can be given the type <code>T a -> Bool</code>, but it can also be given the type <code>T a -> F a</code>, where <code>F Int = Bool</code>. If no such <code>F</code> is in scope at the time of the definition of <code>g</code>, I suppose it <em>does</em> have the principal type you suggest, but predicating the principality of types based on what's in scope is terribly fragile, and so my personal definition of "principal type" includes the possibility of type functions defined later.
</p>
<p>
@tvynr's example is different, I think, in that it seems there is a principal type no matter what other type-level definitions are available. Without thinking about the details, I conjecture that it's possible to detect variables whose scopes are appropriate w.r.t. the available equalities such that we can declare these variables to be touchable, where universally-quantified variables would be untouchable.
</p>
TicketporgesSun, 14 Dec 2014 21:16:19 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:11
http://trac.haskell.org/trac/ghc/ticket/9223#comment:11
<p>
I ran into something similar to this with a type like:
</p>
<blockquote>
<p>
forall t. (forall m. (C m, m ~ t) => m) -> t
</p>
</blockquote>
<p>
This marked 't' untouchable. The solution was to write instead:
</p>
<blockquote>
<p>
forall t. ((C t) => t) -> t
</p>
</blockquote>
<p>
Are there any cases in which these aren't equivalent? If not, why is GHC not able to reduce the first to the second?
</p>
TicketsimonpjMon, 15 Dec 2014 08:33:09 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:12
http://trac.haskell.org/trac/ghc/ticket/9223#comment:12
<p>
@porges: Yes, the two types should be equivalent. GHC HEAD (and 7.10) should be better in this regard (see <a class="closed ticket" href="http://trac.haskell.org/trac/ghc/ticket/9211" title="#9211: bug: Untouchable type variable (regression from 7.6) (closed: fixed)">#9211</a>). Can you try your example with that?
</p>
<p>
@goldire: you may be right. But even if you are, there's a balance between trying to make type inference as clever as possible and making it so unpredictable that no one knows whether it'll work or not. But I'm open to suggetions.
</p>
<p>
Simon
</p>
Ticketdanilo2Sat, 21 Jan 2017 13:30:07 GMT
http://trac.haskell.org/trac/ghc/ticket/9223#comment:13
http://trac.haskell.org/trac/ghc/ticket/9223#comment:13
<p>
I just realized my ticket may be connected to this one! If it is we could merge them: <a class="ext-link" href="https://ghc.haskell.org/trac/ghc/ticket/13161#ticket"><span class="icon"></span>https://ghc.haskell.org/trac/ghc/ticket/13161#ticket</a>
</p>
Ticket