Confused by type constraints

I have some code resembling class Foo f where foo :: a -> f a data Bar f a = Foo f => Bar {bar :: f a} instance Foo (Bar f) where foo a = Bar $ foo a GHC insists on Foo f => in the instance declaration. However, the definition of Bar guarantees that this will always be the case. Why do I have to state this explicitly?

On Fri, 03 Jun 2011 16:04:05 +0300, Guy
I have some code resembling
class Foo f where foo :: a -> f a
data Bar f a = Foo f => Bar {bar :: f a}
instance Foo (Bar f) where foo a = Bar $ foo a
GHC insists on Foo f => in the instance declaration. However, the definition of Bar guarantees that this will always be the case. Why do I have to state this explicitly?
The constraints in the data type declaration only affect the constructors. It's a known issue, unfortunately there doesn't seem to be a way around it. See also: http://www.haskell.org/haskellwiki/Data_declaration_with_constraint Cheers, Daniel

On 03/06/2011 16:55, Daniel Schoepe wrote:
On Fri, 03 Jun 2011 16:04:05 +0300, Guy
wrote: I have some code resembling
class Foo f where foo :: a -> f a
data Bar f a = Foo f => Bar {bar :: f a}
instance Foo (Bar f) where foo a = Bar $ foo a
GHC insists on Foo f => in the instance declaration. However, the definition of Bar guarantees that this will always be the case. Why do I have to state this explicitly?
The constraints in the data type declaration only affect the constructors. It's a known issue, unfortunately there doesn't seem to be a way around it.
It seems to work for simpler examples. For example, this is OK data X x = Show x => X x s :: X x -> String s (X x) = show x I don't have to add a Show x => type constraint to s.

On Fri, Jun 03, 2011 at 05:13:43PM +0300, Guy wrote:
On 03/06/2011 16:55, Daniel Schoepe wrote:
On Fri, 03 Jun 2011 16:04:05 +0300, Guy
wrote: I have some code resembling
class Foo f where foo :: a -> f a
data Bar f a = Foo f => Bar {bar :: f a}
instance Foo (Bar f) where foo a = Bar $ foo a
GHC insists on Foo f => in the instance declaration. However, the definition of Bar guarantees that this will always be the case. Why do I have to state this explicitly?
The constraints in the data type declaration only affect the constructors. It's a known issue, unfortunately there doesn't seem to be a way around it.
It seems to work for simpler examples. For example, this is OK
data X x = Show x => X x
s :: X x -> String s (X x) = show x
I don't have to add a Show x => type constraint to s.
In this case, you are *destructing* an X value, and when you do, the Show constraint on x becomes available, since the X constructor is always paired with a Show constraint. In your original example, you are *constructing* a Bar value. You have stated that the Bar constructor must be accompanied by a Foo constraint, but there is no such constraint in sight! So the difference is between *destructing* a value (when the associated constraint becomes available) and *constructing* one (when the required constraint must be provided). I don't know if I've explained that well. As others have mentioned, the ability to specify constraints on constructors in this way is probably going to be removed from the language anyway. -Brent

On 04/06/2011 22:49, Brent Yorgey wrote:
On Fri, Jun 03, 2011 at 05:13:43PM +0300, Guy wrote:
On 03/06/2011 16:55, Daniel Schoepe wrote:
On Fri, 03 Jun 2011 16:04:05 +0300, Guy
wrote: I have some code resembling
class Foo f where foo :: a -> f a
data Bar f a = Foo f => Bar {bar :: f a}
instance Foo (Bar f) where foo a = Bar $ foo a
GHC insists on Foo f => in the instance declaration. However, the definition of Bar guarantees that this will always be the case. Why do I have to state this explicitly?
The constraints in the data type declaration only affect the constructors. It's a known issue, unfortunately there doesn't seem to be a way around it.
It seems to work for simpler examples. For example, this is OK
data X x = Show x => X x
s :: X x -> String s (X x) = show x
I don't have to add a Show x => type constraint to s.
In this case, you are *destructing* an X value, and when you do, the Show constraint on x becomes available, since the X constructor is always paired with a Show constraint.
In your original example, you are *constructing* a Bar value. You have stated that the Bar constructor must be accompanied by a Foo constraint, but there is no such constraint in sight!
So the difference is between *destructing* a value (when the associated constraint becomes available) and *constructing* one (when the required constraint must be provided).
I don't know if I've explained that well. As others have mentioned, the ability to specify constraints on constructors in this way is probably going to be removed from the language anyway.
So in my original example, why isn't "instance Foo (Bar f)" destructing Bar - and making the constraint available?

On Sun, 05 Jun 2011 19:53:50 +0300, Guy
So in my original example, why isn't "instance Foo (Bar f)" destructing Bar - and making the constraint available?
The "Bar" in "instance Foo (Bar f)" is a type constructor, not a data constructor like the X in "s (X x)". Hence "instance Foo (Bar f)" isn't really deconstructing anything, but applying the type constructor Bar to f, whereas "s (X x)" matches a _value_ of type X. This might be a bit clearer if you name type and data constructors differently, e.g.: data Bar f a = Foo f => MkBar { bar :: f a } Cheers, Daniel

On 06/06/2011 17:31, Daniel Schoepe wrote:
On Sun, 05 Jun 2011 19:53:50 +0300, Guy
wrote: So in my original example, why isn't "instance Foo (Bar f)" destructing Bar - and making the constraint available?
The "Bar" in "instance Foo (Bar f)" is a type constructor, not a data constructor like the X in "s (X x)". Hence "instance Foo (Bar f)" isn't really deconstructing anything, but applying the type constructor Bar to f, whereas "s (X x)" matches a _value_ of type X.
This might be a bit clearer if you name type and data constructors differently, e.g.:
data Bar f a = Foo f => MkBar { bar :: f a }
Cheers, Daniel
Thanks, I (think I) understand your explanation, but ... why can't GHC infer that Bar must always be a Show, seeing as this is the only constructor?

On Tuesday 07 June 2011, 10:51:00, Guy wrote:
On 06/06/2011 17:31, Daniel Schoepe wrote:
On Sun, 05 Jun 2011 19:53:50 +0300, Guy
wrote: So in my original example, why isn't "instance Foo (Bar f)" destructing Bar - and making the constraint available?
The "Bar" in "instance Foo (Bar f)" is a type constructor, not a data constructor like the X in "s (X x)". Hence "instance Foo (Bar f)" isn't really deconstructing anything, but applying the type constructor Bar to f, whereas "s (X x)" matches a _value_ of type X.
This might be a bit clearer if you name type and data constructors differently, e.g.:
data Bar f a = Foo f => MkBar { bar :: f a }
Cheers, Daniel
Thanks, I (think I) understand your explanation, but ... why can't GHC infer that Bar must always be a Show, seeing as this is the only constructor?
undefined :: Bar f a You get the Foo constraint from the constructor MkBar, but there is a value which doesn't use the constructor, so you can't get it from the type alone.

On 07/06/2011 12:03, Daniel Fischer wrote:
On Tuesday 07 June 2011, 10:51:00, Guy wrote:
On 06/06/2011 17:31, Daniel Schoepe wrote:
On Sun, 05 Jun 2011 19:53:50 +0300, Guy
wrote: So in my original example, why isn't "instance Foo (Bar f)" destructing Bar - and making the constraint available?
The "Bar" in "instance Foo (Bar f)" is a type constructor, not a data constructor like the X in "s (X x)". Hence "instance Foo (Bar f)" isn't really deconstructing anything, but applying the type constructor Bar to f, whereas "s (X x)" matches a _value_ of type X.
This might be a bit clearer if you name type and data constructors differently, e.g.:
data Bar f a = Foo f => MkBar { bar :: f a }
Cheers, Daniel
Thanks, I (think I) understand your explanation, but ... why can't GHC infer that Bar must always be a Show, seeing as this is the only constructor?
undefined :: Bar f a
You get the Foo constraint from the constructor MkBar, but there is a value which doesn't use the constructor, so you can't get it from the type alone.
So if GHC were to infer the type constraint, it could be violated by undefined. Would this prevent any real code from working?

On Fri, Jun 3, 2011 at 09:55, Daniel Schoepe
The constraints in the data type declaration only affect the constructors. It's a known issue, unfortunately there doesn't seem to be a way around it.
GADTs handle it as expected. The core problem is with how the Haskell 98 standard declared them to work; the Haskell standards committee has proposed removing them altogether from non-GADT syntax since they're worse than useless as is.
participants (5)
-
Brandon Allbery
-
Brent Yorgey
-
Daniel Fischer
-
Daniel Schoepe
-
Guy