[GHC] #11224: Program doesn't preserve semantics after pattern synonym inlining.

GHC ghc-devs at haskell.org
Wed Dec 16 11:27:14 UTC 2015


#11224: Program doesn't preserve semantics after pattern synonym inlining.
-------------------------------------+-------------------------------------
        Reporter:  anton.dubovik     |                Owner:
            Type:  bug               |               Status:  new
        Priority:  highest           |            Milestone:  8.0.1
       Component:  Compiler          |              Version:  7.10.2
      Resolution:                    |             Keywords:
                                     |  PatternSynonyms
Operating System:  Windows           |         Architecture:  x86_64
 Type of failure:  Incorrect result  |  (amd64)
  at runtime                         |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:  #11225            |  Differential Rev(s):  Phab:D1632
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by simonpj):

 Right.  To typecheck a program involving `P` we absolutely need to know
 the universal/existential split.

 Above, I was thinking that we could use the pattern type signature to
 specify the "shape" of the type, but work out the univ/existential split
 from the definition itself.  A bit like a type with wildcards.  But that
 would mean that you could not typecheck uses of a  pattern synonym solely
 from its signature; because the signature does not say enough.  Like
 wildcards, inference would be involved.  But that's obviously
 unsatisfactory; for example, you could not put a pattern synonym signature
 in a hs-boot file.

 So, if we want signatures to be fully precise (i.e. say everything), and I
 think that is a solidly good goal, we'd need to require the user to
 specify the required/provided split for type variables too.

 Terminology:
  * "Universal" for type variables lines up with "required" for the
 constraints
  * "Existential" for type variables lines up with "provided" for the
 constraints

 Now, we need syntax for expressing the split.  I suppose we can use the
 same nested structure as we do for required/provided, like this:
 {{{
 pattern type P1 :: forall a. a -> T a        -- a is universal

 pattern type P2 :: forall a. Eq a => a -> T  -- a is universal, (Eq a) is
 required

 pattern type P3 :: forall a. Eq a =>         -- a is universal, (Eq a) is
 required
                    forall b. a -> b -> T a   -- b is existential

 pattern type P3 :: forall a. Eq a =>         -- a is universal, (Eq a) is
 required
                    forall b. Ord b =>        -- b is existential, (Ord b)
 is provided
                    a -> b -> T a
 }}}
 Note:

  * I think we could safely leave out the leading (universal) `forall`, in
 the usual way.  That is, the free vars of the entire type are considered
 universal.

  * You can never leave out the existential forall, if it binds any
 variables.

  * How can you distinguish the existential forall if the universal forall
 and context are absent?  Only by putting in the universal forall or the
 required context; e.g.
 {{{
 pattern type P5 :: forall. forall b. b -> T
 pattern type P6 :: () => forall b. b -> T
 }}}
 The only downside of this is that you might say that given
 {{{
 pattern type P :: a -> (a->Int) -> T
 }}}
 the `a` is almost certain to be existential.  After all this is
 the type we'd give to a data constructor:
 {{{
 data T where
   MkT :: (a->Int) -> a -> T
 }}}
 So why do we need explicitly-specified existentials in a pattern
 synonym, but not in a data constructor?  Because a view pattern
 could make it universal.  Degenerately in this case:
 {{{
 pattern Q :: a -> (a->Int) -> String
 pattern Q x y <- (odd_fun -> (x,y))

 odd_fun :: String -> (a, a->Int)
 odd_fun _ = (undefined, const 2)
 }}}

 Conclusion: we need the possibility of explicitly-specified existentials.

 However, rather than insisting that you always specify the
 existential forall, here is a rule that would catch the common cases:
 if you don't specify the existential `forall`, you get:
   * the free vars of the arg-tys
   * plus the free vars of the provided constraints (if any),
   * minus
     * the universal forall'd vars, if there is a universal `forall`
     * or the free vars of the result ty, plus required constraints (if
 any), otherwise

 If neither `forall` is given, we compute the existential variables using
 the above rule; then the universals are the remaining free variables.

 So in the signatures that started this ticket
 {{{
 pattern Q1 :: () => Read a => a -> String
 pattern Q2 :: Read a => a -> String
 }}}
 would behave as if you had written
 {{{
 pattern Q1 :: () => forall a. Read a => a -> String
 pattern Q2 :: forall a. Read a => a -> String
 }}}
 The advantage of this rule is that is allows you to omit
 both `forall`s in all but the most degenerate cases like `Q` above.

 How does that sound?  I think it is more or less what Richard was
 proposing in comment:8, but it's taken me a while to catch up.

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


More information about the ghc-tickets mailing list