Opened 9 years ago

Closed 9 years ago

Last modified 9 years ago

#4338 closed bug (fixed)

weird discrepancies between TFs and FDs in GHC7

Reported by: illissius Owned by:
Priority: high Milestone: 7.4.1
Component: Compiler (Type checker) Version: 6.13
Keywords: Cc:
Operating System: Linux Architecture: x86
Type of failure: Compile-time crash Test Case: indexed_types/should_compile/T4338
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

I'm trying to do some seemingly equivalent code with GHC7 as of 09/19, using TypeFamilies on the one hand and FunctionalDependencies on the other, and my experience is that the TFs version results in some really weird-ass error messages from the compiler -- and a hang in one case -- whereas the FDs version works just fine. I'm not sure about the errors, though they certainly seem bizarre, but I'm pretty sure the compiler hanging is a bug. (And I assume a hang is morally equivalent to a crash, so I'm marking this as such.)

Here's the version with TFs:

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleContexts #-}

class (There a ~ b, BackAgain b ~ a) => Foo a b where
    type There a
    type BackAgain b
    there :: a -> b
    back :: b -> a
    tickle :: b -> b

instance Foo Char Int where
    type There Char = Int
    type BackAgain Int = Char
    there = fromEnum
    back = toEnum
    tickle = (+1)

test :: (Foo a b) => a -> a
test = back . tickle . there

main :: IO ()
main = print $ test 'F'

and the one with FDs:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Bar a b | a -> b, b -> a where
    there :: a -> b
    back :: b -> a
    tickle :: b -> b

instance Bar Char Int where
    there = fromEnum
    back = toEnum
    tickle = (+1)

test :: (Bar a b) => a -> a
test = back . tickle . there

main :: IO ()
main = print $ test 'F'

Are these as functionally-equivalent as they seem, or are there some subtle differences I'm missing? (Is it possible there's some kind of configuration problem on my end?)

In any case, the result is that the TFs version gives me different errors depending on which type signatures I supply or omit, whereas the version with FDs compiles and works correctly in all cases.

The TFs version, if I supply both type signatures (as listed):

$ ghc Foo.hs 
[1 of 1] Compiling Main             ( Foo.hs, Foo.o )

Foo.hs:18:15:
    Could not deduce (Foo (BackAgain (There a)) (There a))
      from the context (Foo a b)
      arising from a use of `tickle'
    Possible fix:
      add (Foo (BackAgain (There a)) (There a)) to the context of
        the type signature for `test'
      or add an instance declaration for
         (Foo (BackAgain (There a)) (There a))
    In the first argument of `(.)', namely `tickle'
    In the second argument of `(.)', namely `tickle . there'
    In the expression: back . tickle . there

Foo.hs:21:16:
    Overlapping instances for Foo Char Int
      arising from a use of `test'
    Matching instances:
      instance Foo Char Int -- Defined at Foo.hs:10:10-21
    (The choice depends on the instantiation of `'
     To pick the first instance above, use -XIncoherentInstances
     when compiling the other instance declarations)
    In the second argument of `($)', namely `test 'F''
    In the expression: print $ test 'F'
    In an equation for `main': main = print $ test 'F'

If I leave off the type signature for main, but not test:

$ ghc Foo.hs 
[1 of 1] Compiling Main             ( Foo.hs, Foo.o )

Foo.hs:18:15:
    Could not deduce (Foo (BackAgain (There a)) (There a))
      from the context (Foo a b)
      arising from a use of `tickle'
    Possible fix:
      add (Foo (BackAgain (There a)) (There a)) to the context of
        the type signature for `test'
      or add an instance declaration for
         (Foo (BackAgain (There a)) (There a))
    In the first argument of `(.)', namely `tickle'
    In the second argument of `(.)', namely `tickle . there'
    In the expression: back . tickle . there

If I leave off the signature for test, regardless of whether I supply one for main:

$ ghc Foo.hs 
[1 of 1] Compiling Main             ( Foo.hs, Foo.o )
^C
-- a seemingly infinite loop

Attachments (2)

Foo.hs (467 bytes) - added by illissius 9 years ago.
Bar.hs (344 bytes) - added by illissius 9 years ago.

Download all attachments as: .zip

Change History (10)

Changed 9 years ago by illissius

Attachment: Foo.hs added

Changed 9 years ago by illissius

Attachment: Bar.hs added

comment:1 Changed 9 years ago by illissius

I did a clean build from HEAD. Bar.hs still builds correctly; Foo.hs now results in a panic irrespective of which type signatures I supply or omit:

$ ghc Foo.hs 
[1 of 1] Compiling Main             ( Foo.hs, Foo.o )
ghc: panic! (the 'impossible' happened)
  (GHC version 7.1.20101008 for i386-unknown-linux):
        topEvBindPairs $dFoo{v agj} [lid]

Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug

comment:2 Changed 9 years ago by simonmar

Milestone: 7.0.1
Priority: normalhighest

comment:3 Changed 9 years ago by simonpj

I'm afraid GHC 7.0 won't handle superclass equalities. We just ran out of time. Sorry. Thanks for the nice example though.

Simon

comment:4 Changed 9 years ago by simonpj

Milestone: 7.0.17.0.2
Priority: highesthigh

comment:5 Changed 9 years ago by illissius

Ah, okay. I was about to ask whether this would be 7.0.2 or 7.2 material :)

comment:6 Changed 9 years ago by simonpj

Milestone: 7.0.27.2.1

HEAD and 7.0 branch gives a civilised message

T4338.hs:5:1:
    Alas, GHC 7.0 still cannot handle equality superclasses:
      There a ~ b
    In the context: (There a ~ b, BackAgain b ~ a)
    While checking the super-classes of class `Foo'
    In the class declaration for `Foo'

Will fix properly in 7.2.

comment:7 Changed 9 years ago by simonpj

Resolution: fixed
Status: newclosed
Test Case: indexed_types/should_compile/T4338

Hurrah! We now have superclass equalities. I've added this example as a test.

commit 940d1309e58382c889c2665227863fd790bdb21c
Author: Simon Peyton Jones <simonpj@microsoft.com>
Date:   Wed Jun 22 17:37:47 2011 +0100

    Add equality superclasses
    
    Hurrah.  At last we can write
    
       class (F a ~ b) => C a b where { ... }
    
    This fruit of the fact that equalities are now values,
    and all evidence is handled uniformly.
    
    The main tricky point is that when translating to Core
    an evidence variable 'v' is represented either as
      either   Var v
      or       Coercion (CoVar v)
    depending on whether or not v is an equality.  This leads
    to a few annoying calls to 'varToCoreExpr'.

 compiler/basicTypes/MkId.lhs      |    4 +-
 compiler/deSugar/DsExpr.lhs       |   18 +++++++-
 compiler/iface/BuildTyCl.lhs      |   50 ++++++++++------------
 compiler/typecheck/TcInstDcls.lhs |   85 +++++++++++++++++-------------------
 compiler/typecheck/TcMType.lhs    |    9 +----
 compiler/types/Class.lhs          |   21 ++++------
 6 files changed, 92 insertions(+), 95 deletions(-)

comment:8 Changed 9 years ago by illissius

yay! :)

Note: See TracTickets for help on using tickets.