Основной вопрос: какие принципы проектирования следует придерживаться при выборе между использованием класса или использованием записи (с полиморфными полями)?
Во-первых, мы знаем, что классы и записи по существу эквивалентны (так как в Core классы попадают в словари, которые являются просто записями). Тем не менее, существуют различия: классы передаются неявно, записи должны быть явными.
Глядя немного глубже, классы действительно полезны, когда:
- у нас много разных представлений о "одном и том же", а
- в фактическом использовании, использование которого может быть выведено.
Классы неудобны, когда мы имеем (до параметрического полиморфизма) только одно представление наших данных, но у нас есть несколько экземпляров. Это приводит к синтаксическому шуму использования newtype для добавления дополнительных тегов (которые существуют только в нашем коде, поскольку мы знаем, что такие теги стираются во время выполнения), если мы не хотим включать всевозможные неприятные расширения (т.е. перекрывающиеся и/или неразрешимые экземпляры).
Конечно, все становится мутным: что, если я хочу иметь ограничения на свои типы? Выберем реальный пример:
class (Bounded i, Enum i) => Partition a i where
index :: a -> i
Я мог бы так же легко сделать
data Partition a i = Partition { index :: a -> i}
Но теперь я потерял свои ограничения, и мне придется добавлять их к определенным функциям.
Есть ли рекомендации по дизайну, которые помогут мне?