что ограничивает, чтобы вы не могли сопоставить шаблон с конструкторами с ограниченным параметром класса типа?
Когда вы сопоставляете шаблон с явным конструктором, вы фиксируете представление определенного типа данных. Этот тип данных не является общим для всех экземпляров класса, и поэтому просто невозможно написать функцию, которая будет работать для всех экземпляров таким образом.
Вместо этого вам нужно связать различные типы поведения, которые вам нужны, с каждым экземпляром, например:
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
Ветви вашей исходной функции test
теперь распределены по экземплярам.
Некоторые альтернативные приемы включают использование типов данных, связанных с классом (где вы доказываете компилятору, что тип данных является общим для всех экземпляров), или просмотр шаблонов (что позволяет вам обобщить сопоставление с образцом).
Просмотр шаблонов
Мы можем использовать шаблоны представлений, чтобы немного очистить связь между сопоставлением с образцом и экземплярами класса типов, что позволит нам приблизить сопоставление с образцом между экземплярами путем сопоставления с образцом для общего типа.
Вот пример, где мы пишем одну функцию с двумя падежами, которая позволяет нам сопоставлять паттерны с чем угодно в классе.
{-# 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
Обратите внимание, что синтаксис ->
позволяет нам в каждом случае возвращаться к правильной функции view
, просматривая пользовательский тип данных, кодирующий тип, для сопоставления с ним по шаблону.
Задача разработки состоит в том, чтобы придумать тип представления, который охватывает все варианты поведения, которые вас интересуют.
В своем исходном вопросе вы хотели, чтобы каждый конструктор имел свое поведение, поэтому на самом деле нет причин использовать тип представления (отправка непосредственно этому поведению в каждом экземпляре уже работает достаточно хорошо).