GHC, functional dependency, rank-2 type
There appears to be a problem with GHC's handling of multi-parameter constraints with functional dependencies in the context of rank-2 types. The problem is not present in Hugs. Let us first consider a simple example:
module Test where class Foo a
class Bar a
data Obj = Obj
instance Bar Obj
instance (Bar a) => Foo a
foo:: (Foo a) => a -> String foo _ = "works"
runFoo:: (forall a. (Foo a) => a -> w) -> w runFoo f = f Obj
Here, the type of foo indicates
Test> :t foo foo :: Foo a => a -> String
that foo can be applied to the value of any type so long as it is in class Foo. The type of runFoo says that runFoo takes a function that can process the value of every type of class Foo. The function foo seems to be such a function. Therefore, "runFoo foo" should be well-typed. And it is, and it works, both in Hugs (version November 2002) and in GHC 6.0.1 (which is the current version for FreeBSD). However, if we make the classes Foo and Bar seemingly multi-parameter, the trouble begins:
class Foo a b | a->b
class Bar a b | a->b
data Obj = Obj
instance Bar Obj Obj
instance (Bar a b) => Foo a b
foo:: (Foo a b) => a -> String foo _ = "works"
runFoo:: (forall a b. (Foo a b) => a -> w) -> w runFoo f = f Obj
In Hugs:
Test> :t foo foo :: Foo a b => a -> String Test> runFoo foo "works"
As before, the function foo promises to handle the value of any type a so long as "Foo a b" is satisfied (for any b -- although there can be only one such b, due to the functional dependency). The function runFoo takes any function that can handle the value of any type "a" subject to the "Foo a b" constraint. So, "runFoo foo" should be well-typed and should work. And it is, in Hugs. GHC 6.01 is a different matter, unfortunately. An attempt to load the above code in GHC succeeds, with flags: {-# OPTIONS -fglasgow-exts -fallow-undecidable-instances #-} Furthermore, the types of foo and runFoo seem to be in perfect match.
*Test> :t foo foo :: forall a b. (Foo a b) => a -> String *Test> :t runFoo runFoo :: forall w. (forall a b. (Foo a b) => a -> w) -> w
And yet, *Test> runFoo foo <interactive>:1: Could not deduce (Bar a b) from the context (Foo a b) arising from use of `foo' at <interactive>:1 Probable fix: Add (Bar a b) to the expected type of an expression In the first argument of `runFoo', namely `foo' In the definition of `it': it = runFoo foo Why all of the sudden does GHC need the constraint Bar a b? The function foo didn't ask for that... However, if we define foo and runFoo as follows:
foo:: (Foo a b) => a -> b -> String foo _ _ = "works"
runFoo:: (forall a b. (Foo a b) => a -> b -> w) -> w runFoo f = f Obj Obj
then both GHC and Hugs are happy: "runFoo foo" says that it works in both. What's more bizarre, even the following works:
runFoo:: (forall a b. (Foo a b) => a -> b -> w) -> w runFoo f = f Obj undefined
oleg@pobox.com wrote:
There appears to be a problem with GHC's handling of multi-parameter constraints with functional dependencies in the context of rank-2 types. The problem is not present in Hugs.
Let us first consider a simple example:
module Test where class Foo a
class Bar a
data Obj = Obj
instance Bar Obj
instance (Bar a) => Foo a
I have tried to write code myself like in the last line, stating that any instance of one class should also be an instance of another class. But Hugs would complain about it and so it does about the above: Prelude> :l Test Reading file "Test.hs": ERROR Test.hs:10 - syntax error in instance head (constructor expected) This is the Feb2001 version of hugs running on RedHat9. I seem to remember that the Haskell Report indeed states that the right side of an instance declaration should contain a type constructor. So I'm puzzled as to why the above works for you... regards, %!PS % -John Tromp (http://www.cwi.nl/~tromp/) 42 42 scale 7 9 translate .07 setlinewidth .5 setgray/c{arc clip fill setgray}def 1 0 0 42 1 0 c 0 1 1{0 3 3 90 270 arc 0 0 6 0 -3 3 90 270 arcn 270 90 c -2 2 4{-6 moveto 0 12 rlineto}for -5 2 5{-3 exch moveto 9 0 rlineto}for stroke 0 0 3 1 1 0 c 180 rotate initclip}for showpage
John Tromp wrote:
instance (Bar a) => Foo a
I have tried to write code myself like in the last line, stating that any instance of one class should also be an instance of another class. But Hugs would complain about it.
You're right. Hugs needs to be told that the code is in an extended Haskell, with popular extensions. Therefore, Hugs has to invoked as "hugs -98". It seems -98 flag cannot be changed once the Hugs is running. Incidentally, the previously posted code included rank-2 types -- which are flagrantly not in Haskell98. Therefore, without -98 flag, Hugs will have a lot to complain about.
participants (2)
-
John Tromp -
oleg@pobox.com