what is it that restricts so you can't pattern match on constructors with a type class constrained parameter?
When you pattern match on an explicit constructor, you commit to a specific data type representation. This data type is not shared among all instances of the class, and so it is simply not possible to write a function that works for all instances in this way.
Instead, you need to associate the different behaviors your want with each instance, like so:
class C a where
toString :: a -> String
draw :: a -> String
instance C MyType1 where
toString v = "MyType1"
draw (MyObj11 x) = "11"
draw (MyObj12 x y) = "12"
instance C MyType2 where
toString v = "MyType2"
draw (MyObj22 x y) = "22"
data MyType1 = MyObj11 Int | MyObj12 Int Int
data MyType2 = MyObj21 Int | MyObj22 Int Int
test :: C a => a -> String
test x = draw x
The branches of your original test
function are now distributed amongst the instances.
Some alternative tricks involve using class-associated data types (where you prove to the compiler that a data type is shared amongst all instances), or view patterns (which let you generalize pattern matching).
View patterns
We can use view patterns to clean up the connection between pattern matching and type class instances, a little, allowing us to approximate pattern matching across instances by pattern matching on a shared type.
Here's an example, where we write one function, with two cases, that lets us pattern match against anything in the class.
{-# LANGUAGE ViewPatterns #-}
class C a where
view :: a -> View
data View = One Int
| Two Int Int
data MyType1 = MyObj11 Int | MyObj12 Int Int
instance C MyType1 where
view (MyObj11 n) = One n
view (MyObj12 n m) = Two n m
data MyType2 = MyObj21 Int | MyObj22 Int Int
instance C MyType2 where
view (MyObj21 n) = One n
view (MyObj22 n m) = Two n m
test :: C a => a -> String
test (view -> One n) = "One " ++ show n
test (view -> Two n m) = "Two " ++ show n ++ show m
Note how the ->
syntax lets us call back to the right view
function in each instance, looking up a custom data type encoding per-type, in order to pattern match on it.
The design challenge is to come up with a view type that captures all the behavior variants you're interested in.
In your original question, you wanted every constructor to have a different behavior, so there's actually no reason to use a view type (dispatching directly to that behavior in each instance already works well enough).
test
do if given a value of typeSomeoneElsesType
wheninstance SomeClass SomeoneElsesType where toString v = "mwahahahah"
? dons answer is the proper way to handle this. - rampion