[GHC] #11243: Flag to not expand type families

GHC ghc-devs at haskell.org
Fri Dec 18 11:49:30 UTC 2015


#11243: Flag to not expand type families
-------------------------------------+-------------------------------------
        Reporter:  crockeea          |                Owner:
            Type:  feature request   |               Status:  new
        Priority:  normal            |            Milestone:  8.2.1
       Component:  Compiler          |              Version:  7.10.2
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by simonpj):

 The underlying issue is this:

 * Suppose `F` is a type function with an instance for `F [Int]`. If we
 have constraints
    1. `(C (F [Int]))`, where `C` is a class
    2. `F [Int] ~ Maybe Bool`
    3. `G (F [Int]) ~ Maybe Bool`
    then we want to apply the instance in case that unlocks the class
 constraint or equality.

 * That expansion may be fruitless.  For (1), perhaps `F [Int]` expands to
 `Bool`, `C` has no `Bool` instance.  Then would you prefer to see "can't
 solve `C (F [Int])`" or "can't solve `C Bool`?  Perhaps the latter.

 * In (3) we might expand `F [Int]` vigorously to get a big type, and
 ''still'' not be able to simplify the call to `G`.

 In the case of type synonyms we can have the best of both worlds.  Say we
 have
 {{{
 type T a = S a a
 }}}
 Then the type `T [Int]` is represented (essentially) as a pair of its
 expanded and un-expanded form;
 so we can freely choose to use either at any time.

 But it's not so easy for type functions becuase the expanded and un-
 expanded forms are not
 interchangeable; there's a coercion involved.

 I think we could still be a bit less aggressive about expansion.  We even
 implemted this once.  But we disabled it for ill-understood performance
 reasons.
 Here is the Note from `TcFlatten`:
 {{{
 Note [Lazy flattening]
 ~~~~~~~~~~~~~~~~~~~~~~
 The idea of FM_Avoid mode is to flatten less aggressively.  If we have
        a ~ [F Int]
 there seems to be no great merit in lifting out (F Int).  But if it was
        a ~ [G a Int]
 then we *do* want to lift it out, in case (G a Int) reduces to Bool, say,
 which gets rid of the occurs-check problem.  (For the flat_top Bool, see
 comments above and at call sites.)

 HOWEVER, the lazy flattening actually seems to make type inference go
 *slower*, not faster.  perf/compiler/T3064 is a case in point; it gets
 *dramatically* worse with FM_Avoid.  I think it may be because
 floating the types out means we normalise them, and that often makes
 them smaller and perhaps allows more re-use of previously solved
 goals.  But to be honest I'm not absolutely certain, so I am leaving
 FM_Avoid in the code base.  What I'm removing is the unique place
 where it is *used*, namely in TcCanonical.canEqTyVar.

 See also Note [Conservative unification check] in TcUnify, which gives
 other examples where lazy flattening caused problems.

 Bottom line: FM_Avoid is unused for now (Nov 14).
 Note: T5321Fun got faster when I disabled FM_Avoid
       T5837 did too, but it's pathalogical anyway
 }}}
 In short, here's a project for someone!

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


More information about the ghc-tickets mailing list