Ticket #2187 (closed bug: wontfix)

Opened 4 years ago

Last modified 5 months ago

Top-level bindings are broken for polymorphic values

Reported by: yallop Owned by:
Priority: normal Milestone: 6.10 branch
Component: Compiler Version: 6.8.2
Keywords: Cc: SamB
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

Top-level pattern bindings don't work for polymorphic values. Here's an example program:

    [x] = [id]

ghci gives this the type

   *Main> :t x
   x :: GHC.Prim.Any -> GHC.Prim.Any

which can't be used

   *Main>  x ()

   <interactive>:1:2:
   Couldn't match expected type `GHC.Prim.Any'
          against inferred type `()'
   In the first argument of `x', namely `()'
   In the expression: x ()
   In the definition of `it': it = x ()

Adding a type signature doesn't help:

   x :: a -> a
   [x] = [id]

   Couldn't match expected type `forall a. a -> a'
          against inferred type `a -> a'
   Probable cause: `id' is applied to too few arguments
   In the expression: id
   In the expression: [id]

Change History

  Changed 4 years ago by igloo

  • difficulty set to Unknown
  • milestone set to 6.10 branch

Thanks for the report. Your first example is deliberately broken (relative to Haskell98); see http://www.haskell.org/ghc/docs/latest/html/users_guide/monomorphism.html for details, or use -XNoMonoPatBinds to get the Haskell98-specified behaviour.

If you give a type signature for x then I would have expected it to work; Simon?

yallop, I assume you ran into this problem in a real program?

  Changed 4 years ago by yallop

Thanks for the reply. Yes, I ran into the problem in a real program.

I have code that looks like this:

   (x1,...,xn) = (e1,...en)
      where y = e
            z = f

which is generated via Template Haskell. Sometimes the bindings are supposed to be polymorphic, sometimes not.

It looks like I have two choices, neither of which is particularly desirable:

  1. Change the generation scheme to emit less elegant code, which will also complicate the generation code.
  2. Require all users of the library to add -XNoMonoPatBinds

From a user's perspective (at least, from this user's), defaulting to monomorphic pattern bindings looks like quite an unfortunate choice:

  1. It doesn't bring any actual benefits to the user (that I can see).
  2. It breaks compatibility with Haskell 98, so code that works on other implementations mysteriously fails without so much as a warning.
  3. It generates quite unusable code: there's no realistic use for functions of type
            GHC.Prim.Any -> GHC.Prim.Any
    
    so why default to generating that sort of thing?
  4. It makes it more difficult to transform programs, since you can no longer replace
           x = f (a,b)
    
    with
           (c,d) = (a,b)
           f (c,d)
    

It's difficult to see how it can simplify the implementation, since you still have to cope with -XNoMonoPatBinds. There must be *some* reason for this change, but what can it be?

  Changed 4 years ago by simonmar

Linking to the Haskell' page with the motivation for this change:  http://hackage.haskell.org/cgi-bin/haskell-prime/trac.cgi/wiki/MonomorphicPatternBindings

As for whether it makes the implementation simpler, I presume the intention was to deprecate and eventually remove -XNoMonoPatBinds at which point the complexities to deal with polymorphic pattern bindings would go away. (however we can't do that while we still want to support Haskell 98, of course). I'm going to follow up on this with the Haskell' committee.

  Changed 4 years ago by SamB

  • cc SamB added

*more* ugly warts? I thought we were trying to get *rid* of those ...

Any bets on which 6.10.x release will revert this?

  Changed 4 years ago by simonmar

  • status changed from new to closed
  • resolution set to wontfix

So GHC is behaving "correctly" here, although for Haskell' we've decided to do things differently; see #2357. Hopefully we'll be able to implement the change for 6.10.1.

  Changed 3 years ago by simonmar

  • architecture changed from Unknown to Unknown/Multiple

  Changed 3 years ago by simonmar

  • os changed from Unknown to Unknown/Multiple

follow-up: ↓ 9   Changed 9 months ago by MartijnVanSteenbergen

yallop, what package were you using this for? Is it on hackage or in a public repo?

in reply to: ↑ 8   Changed 9 months ago by yallop

Replying to MartijnVanSteenbergen:

yallop, what package were you using this for? Is it on hackage or in a public repo?

It's not publicly-available code, unfortunately.

  Changed 6 months ago by simonpj@…

commit 49dbe60558deee5ea6cd2c7730b7c591d15559c8

Author: Simon Peyton Jones <simonpj@microsoft.com>
Date:   Tue Aug 16 10:23:52 2011 +0100

    Major improvement to pattern bindings
    
    This patch makes a number of related improvements
    
    a) Implements the Haskell Prime semantics for pattern bindings
       (Trac #2357).  That is, a pattern binding p = e is typed
       just as if it had been written
            t = e
            f = case t of p -> f
            g = case t of p -> g
            ... etc ...
       where f,g are the variables bound by p. In paricular it's
       ok to say
          (f,g) = (\x -> x, \y -> True)
       and f and g will get propertly inferred types
          f :: a -> a
          g :: a -> Int
    
    b) Eliminates the MonoPatBinds flag altogether.  (For the moment
       it is deprecated and has no effect.)  Pattern bindings are now
       generalised as per (a).  Fixes Trac #2187 and #4940, in the
       way the users wanted!
    
    c) Improves the OutsideIn algorithm generalisation decision.
       Given a definition without a type signature (implying "infer
       the type"), the published algorithm rule is this:
          - generalise *top-level* functions, and
          - do not generalise *nested* functions
       The new rule is
          - generalise a binding whose free variables have
            Guaranteed Closed Types
          - do not generalise other bindings
    
       Generally, a top-level let-bound function has a Guaranteed
       Closed Type, and so does a nested function whose free vaiables
       are top-level functions, and so on. (However a top-level
       function that is bitten by the Monomorphism Restriction does
       not have a GCT.)
    
       Example:
         f x = let { foo y = y } in ...
       Here 'foo' has no free variables, so it is generalised despite
       being nested.
    
    d) When inferring a type f :: ty for a definition f = e, check that
       the compiler would accept f :: ty as a type signature for that
       same definition.  The type is rejected precisely when the type
       is ambiguous.
    
       Example:
          class Wob a b where
            to :: a -> b
            from :: b -> a
    
          foo x = [x, to (from x)]
       GHC 7.0 would infer the ambiguous type
          foo :: forall a b. Wob a b => b -> [b]
       but that type would give an error whenever it is called; and
       GHC 7.0 would reject that signature if given by the
       programmer.  The new type checker rejects it up front.
    
       Similarly, with the advent of type families, ambiguous types are
       easy to write by mistake.  See Trac #1897 and linked tickets for
       many examples.  Eg
          type family F a :: *
          f ::: F a -> Int
          f x = 3
       This is rejected because (F a ~ F b) does not imply a~b.  Previously
       GHC would *infer* the above type for f, but was unable to check it.
       Now even the inferred type is rejected -- correctly.
    
    The main implemenation mechanism is to generalise the abe_wrap
    field of ABExport (in HsBinds), from [TyVar] to HsWrapper. This
    beautiful generalisation turned out to make everything work nicely
    with minimal programming effort.  All the work was fiddling around
    the edges; the core change was easy!

 compiler/deSugar/DsBinds.lhs        |   62 +++-----
 compiler/deSugar/DsExpr.lhs         |    6 +-
 compiler/hsSyn/HsBinds.lhs          |   28 +++-
 compiler/hsSyn/HsUtils.lhs          |   32 +++--
 compiler/main/DynFlags.hs           |   13 +-
 compiler/rename/RnBinds.lhs         |    6 +-
 compiler/typecheck/TcBinds.lhs      |  282 ++++++++++++++++++++--------------
 compiler/typecheck/TcClassDcl.lhs   |   12 +-
 compiler/typecheck/TcEnv.lhs        |  117 +++++++++------
 compiler/typecheck/TcErrors.lhs     |   32 +++--
 compiler/typecheck/TcHsSyn.lhs      |   14 +-
 compiler/typecheck/TcInstDcls.lhs   |   19 ++-
 compiler/typecheck/TcMType.lhs      |    6 +-
 compiler/typecheck/TcRnDriver.lhs   |   14 +-
 compiler/typecheck/TcRnMonad.lhs    |    2 +-
 compiler/typecheck/TcRnTypes.lhs    |   11 +-
 compiler/typecheck/TcSimplify.lhs   |   57 +++++---
 compiler/typecheck/TcTyClsDecls.lhs |    4 +-
 compiler/typecheck/TcType.lhs       |   28 ++--
 19 files changed, 439 insertions(+), 306 deletions(-)

  Changed 6 months ago by simonpj

OK we're good. Pattern bindings are now generalised as specified in the Haskell Prime spec (see #2357) which is what you originally asked for. -XMonoPatBinds is dead. Everyone is happy.

Simon

  Changed 5 months ago by yallop

That's great. Thank you!

Note: See TracTickets for help on using tickets.