[GHC] #15743: Nail down the Required/Inferred/Specified story

GHC ghc-devs at haskell.org
Mon Oct 29 03:34:49 UTC 2018


#15743: Nail down the Required/Inferred/Specified story
-------------------------------------+-------------------------------------
        Reporter:  simonpj           |                Owner:  (none)
            Type:  bug               |               Status:  closed
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  8.6.1
      Resolution:  fixed             |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
                                     |  dependent/should_fail/T15743[cd],
                                     |  dependent/should_compile/T15743{,e},
                                     |  ghci/scripts/T15743b
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------
Changes (by goldfire):

 * testcase:   =>
     dependent/should_fail/T15743[cd], dependent/should_compile/T15743{,e},
     ghci/scripts/T15743b
 * status:  new => closed
 * resolution:   => fixed


Comment:

 The design decisions are written in

 {{{
 {- Note [Required, Specified, and Inferred for types]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 We have some design choices in how we classify the tyvars bound
 in a type declaration. (Here, I use "type" to refer to any TyClDecl.)
 Much of the debate is memorialized in #15743. This Note documents
 the final conclusion.

 First, a reminder:
   * a Required argument is one that must be provided at every call site
   * a Specified argument is one that can be inferred at call sites, but
     may be instantiated with visible type application
   * an Inferred argument is one that must be inferred at call sites; it
     is unavailable for use with visible type application.

 Why have Inferred at all? Because we just can't make user-facing promises
 about the ordering of some variables. These might swizzle around even
 between
 minor released. By forbidding visible type application, we ensure users
 aren't caught unawares. See also
 Note [VarBndrs, TyCoVarBinders, TyConBinders, and visibility] in TyCoRep.

 When inferring the ordering of variables (that is, for those
 variables that he user has not specified the order with an explicit
 `forall`)
 we use the following order:

  1. Inferred variables from an enclosing class (associated types only)
  2. Specified variables from an enclosing class (associated types only)
  3. Inferred variables not from an enclosing class
  4. Specified variables not from an enclosing class
  5. Required variables before a top-level ::
  6. All variables after a top-level ::

 If this ordering does not make a valid telescope, we reject the
 definition.

 This idea is implemented in the generalise function within kcTyClGroup
 (for
 declarations without CUSKs), and in kcLHsQTyVars (for declarations with
 CUSKs). Note that neither definition worries about point (6) above, as
 this
 is nicely handled by not mangling the res_kind. (Mangling res_kinds is
 done
 *after* all this stuff, in tcDataDefn's call to tcDataKindSig.) We can
 easily tell Inferred apart from Specified by looking at the scoped tyvars;
 Specified are always included there.

 One other small open question here: how to classify variables from an
 enclosing class? Here is an example:

   class C (a :: k) where
     type F a

 In the kind of F, should k be Inferred or Specified? Currently, we mark
 it as Specified, as we can commit to an ordering, based on the ordering
 of class variables in the enclosing class declaration. If k were not
 mentioned
 in the class head, then it would be Inferred. The alternative to this
 approach is to make the Inferred/Specified distinction locally, by just
 looking at the declaration for F. This lowers the availability of type
 application, but makes the reasoning more local. However, this alternative
 also disagrees with the treatment for methods, where all class variables
 are Specified, regardless of whether or not the variable is mentioned in
 the
 method type.

 A few points of motivation for the ordering above:

 * We put the class variables before the local variables in a nod to the
   treatment for class methods, where class variables (and the class
 constraint)
   come first. While this is an unforced design decision, it never rejects
   more declarations, as class variables can never depend on local
 variables.

 * We rigidly require the ordering above, even though we could be much more
   permissive. Relevant musings are at
   https://ghc.haskell.org/trac/ghc/ticket/15743#comment:7
   The bottom line conclusion is that, if the user wants a different
 ordering,
   then can specify it themselves, and it is better to be predictable and
 dumb
   than clever and capricious.

   I (Richard) conjecture we could be fully permissive, allowing all
 classes
   of variables to intermix. We would have to augment ScopedSort to refuse
 to
   reorder Required variables (or check that it wouldn't have). But this
 would
   allow more programs. See #15743 for examples. Interestingly, Idris seems
   to allow this intermixing. The intermixing would be fully specified, in
 that
   we can be sure that inference wouldn't change between versions. However,
   would users be able to predict it? That I cannot answer.

 Test cases (and tickets) relevant to these design decisions:
   T15591*
   T15592*
   T15743*

 -}
 }}}

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


More information about the ghc-tickets mailing list