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.