Apparently it\'s a bad idea to put a typeclass constraint on a data declaration [src], [src].
I haven\'t personally come across a desire to constrain the types withi
It's not actually a bad idea to add a typeclass constraint on a data type - it can be very useful, and doesn't break your other code.
The badness is all about the fact that often people expect that they can then use the data type to excuse them from putting a constraint on functions that use the data type, but that's not the case. (You could argue that an implicit constraint can cause problems.)
Putting a constraint on a datatype actually puts it on the all the constructors that mention the constrained type. Just as with an ordinary function with a constraint, if you use the constructor, you must add the constraint. I think that's healthy and above board.
It does ensure you can't put data in your data type unless you can do certain things with it. Its useful. You won't be creating a programming faux pas by using one, and it's not bad practice, it's just not as lovely as they wanted.
The "bad idea to allow" is probably because GADTs is really what they would like.
If GADTs had been around first, they wouldn't have done this.
I don't think it's such a bad thing to have both. If you want a state manipulating function, you can use a permanently explicit parameter you pass around, or you can use a monad and make it implicit. If you want a constraint on data you can use a permanently explicit one on a data declaration or an implicit one with a GADT. GADTs and monads are more sophisticated, but it doesn't make explicit parameters or data type constraints wrong.
I haven't personally come across a desire to constrain the types within data types I've created, but it's not obvious to me why the language designers "decided it was a bad idea to allow". Why is that?
Because it was misleading and worked completely backwards from what would actually be useful.
In particular, it didn't actually constrain the types within the data type in the way you're probably expecting. What it did do was put a class constraint on the data constructor itself, which meant that you needed to satisfy the instance when constructing a value... but that was all.
So, for instance, you couldn't simply define a binary search tree with an Ord
constraint and then know that any tree has sortable elements; the lookup and insert functions would still need an Ord
constraint themselves. All you'd prevent would be constructing an empty tree that "contains" values of some non-ordered type. As far as pattern matching was concerned, there was no constraint on the contained type at all.
On the other hand, the people working on Haskell didn't think that the sensible version (that people tended to assume data type contexts provided) was a bad idea at all! In fact, class constraints on a data type declared with GADT syntax (generalized algebraic data types, enabled in GHC with the GADTs
language pragma) do work in the obvious way--you need a constraint to construct the value, and the instance in question also gets stored in the GADT, so that you don't need a constraint to work with values, and pattern matching on the GADT constructor lets you use the instance it captured.