[GHC] #9200: Milner-Mycroft failure at the kind level
GHC
ghc-devs at haskell.org
Wed Jun 18 18:40:21 UTC 2014
#9200: Milner-Mycroft failure at the kind level
----------------------------------------------+----------------------------
Reporter: ekmett | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler (Type checker) | Version: 7.8.2
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: GHC rejects valid program | Unknown/Multiple
Test Case: | Difficulty: Unknown
Blocking: | Blocked By:
| Related Tickets:
----------------------------------------------+----------------------------
Comment (by goldfire):
Hold the phone! This is all wrong!
== Interaction with partial type signatures ==
I just ended a great discussion with Iavor and with David Darais about
this whole issue, and we came to the conclusion that (PARGEN) is a bad
idea. The problem isn't that (PARGEN) doesn't work (I still think it
''does'' work) but that it paints us into a corner.
We looked at PartialTypeSignatures, which describes an upcoming feature to
allow wildcards in type signatures of terms, in case the programmer does
not want to write down an entire type. With partial type signatures,
wildcards ''can'' unify with lexically-quantified variables, contrary to
the side condition debated in this ticket. And, the feature looks useful,
as types get more complicated and the burden of writing out a full
signature increases.
There is no discussion (that I saw) of polymorphic recursion w.r.t.
partial type signatures, so I don't know what the term-level plan is on
that issue.
== A change of heart ==
Looking at all of this (now back at the type level), I see that we have
two, mutually exclusive options:
1) Allow polymorphic recursion in the presence of a partial kind signature
via (PARGEN) + side condition.
2) Allow more flexible partial kind signatures.
I think (2) is of greater benefit to users than is (1). As a further
bonus, (2) is '''much''' simpler than (1) -- indeed, it is what we have
today.
== A new proposal ==
Thus, I propose a radically different solution to the original problem:
come up with a syntax for a CUSK for classes. Call this proposal
(NEWCUSK). Specifically:
'''A class or datatype is said to have a CUSK if and only if all of its
type variables are annotated.''' Otherwise, like (BASELINE).
As with (BASELINE), polymorphic recursion is possible only in the presence
of a CUSK. Under (NEWCUSK), Edward's original example will not work, but
adding an annotation to `a` (presumably, `(a :: k2)`) would make it work.
(NEWCUSK) means that a declaration like `data Foo a b :: *` will '''not'''
have a CUSK. This would be a breaking change if a programmer used `:: *`
to force other variables to have kind `*` and/or used polymorphic
recursion.
Note that in the case where a user puts the `::` right after the datatype
name, there are no type variables, so every one is vacuously annotated --
no breakage here.
If we are opposed to making this breaking change, the (NEWCUSK) proposal
still works while also having a top-level `::` indicate a CUSK. I just
think that the top-level `::` syntax is a strange bump in the language
design. (Though, to be fair, I couldn't think of something better at the
time.)
== Relationship to GADT pattern-matching ==
In continuing to think about all this, I have come to realize that
(PARGEN) is quite strange, indeed. Let's consider some more familiar
territory:
{{{
data G a where
GBool :: G Bool
foo GBool = True
}}}
GHC rightly rejects the code above saying, essentially, that `foo` does
not have a principal type. `foo :: G a -> a` and `foo :: G a -> Bool` are
both very reasonable candidates and are not comparable. (The error
message, of course, discusses untouchables... but it's really talking
about principal types.)
But, if we apply the (PARGEN) philosophy to type-checking GADT pattern-
matches, we would decide that `foo :: G a -> Bool` ''is'' the principal
type. This is because the (PARGEN) philosophy would say to prevent the
inferred result type from mentioning any type variables refined in the
pattern match. I am playing a little fast and loose here talking about the
un-articulated "(PARGEN) philosophy", but I'm hoping readers can
understand my concern. A perhaps tighter way of saying this is: I
conjecture that the over-permissiveness of the type system in Fig. 10 (p.
25) of [http://msr-waypoint.com/en-us/um/people/simonpj/papers/constraints
/jfp-outsidein.pdf OutsideIn] (as discussed in Sec. 4.4) might be fixed by
adding a side condition to the `Case` rule, essentially saying that any
type variables refined in a local equality assumption cannot then appear
in other types discovered by using constraints generated by the `case`. To
make this meaningful, the type system would have to be re-written in a
bidirectional manner, so we can know which types come "from" the `case`
vs. which types go "to" the `case`. I leave the exact formulation as
future work, but I'd be very curious to read a response to this
conjecture.
== Conclusion ==
What do others think of (NEWCUSK) and the points I've brought up here? It
seems to solve the original problem (no polymorphic recursion available in
classes) while not upsetting too much else. And, it seems to have nicer
theoretical properties and is easier to explain to users.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/9200#comment:19>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list