Должен ли я использовать класс или нет?
У меня есть некоторые трудности, чтобы понять, когда вы используете и когда не используете typeclass в моем коде. Я имею в виду создать свой собственный, а не использовать уже определенные типы классов, конечно. Например (очень глупый пример), должен ли я делать:
data Cars = Brakes | Wheels | Engine
data Computers = Processor | RAM | HardDrive
class Repairable a where
is_reparaible :: a -> Bool
instance Repairable Cars where
is_repairable (Brakes) = True
is_repairable (Wheels) = False
is_repairable (Engine) = False
instance Repairable Computers where
is_repairable (Processor) = False
is_repairable (RAM) = False
is_repairable (HardDrive) = True
checkState :: (Reparaible a) => a -> ...
checkState a = ...
(Очевидно, это глупый, неполный пример).
Но это много для небольшого использования, нет? Почему я не должен делать что-то простое и только определяя функции без определения новых типов данных и типов (с их экземплярами).
Этот пример слишком прост, но в фактах я часто вижу подобные вещи (новые типы данных + типы + экземпляры), когда я просматриваю код Haskell на github вместо определения только функций.
Итак, когда мне нужно создавать новые типы данных, типы и т.д., и когда я должен использовать функции?
Спасибо.
Ответы
Ответ 1
Почему я не должен делать что-то простое и только определяя функции без определения новых типов данных и типов (с их экземпляры).
Почему? Вы можете просто определить:
checkState :: (a -> Bool) -> (a -> b) -> (a -> b) -> a -> b
checkState is_repairable repairs destroy a
= if (is_repairable a) then repairs a else destroy a
Люди неправильно используют классы классов. Это не значит, что он идиоматичен.
Чтобы ответить на ваш более общий вопрос, вот некоторые эмпирические правила о том, когда использовать классы типов и когда их не использовать:
Используйте классы типов, если:
-
Существует только одно правильное поведение для заданного типа
-
Класс типа имеет связанные уравнения (т.е. "законы" ), что все экземпляры должны удовлетворять
Не используйте классы типов, если:
-
Вы пытаетесь просто использовать пространства имен. Для чего нужны модули и пространства имен.
-
Человек, использующий ваш тип, не может рассуждать о том, как он будет вести себя, не глядя на исходный код экземпляров
-
Вы обнаружите, что расширения, которые вы должны включить, выходят из-под контроля
Ответ 2
Вы можете часто использовать тип данных вместо типа-типа, например
data Repairable a = Repairable
{ getRepairable :: a
, isRepairable :: Bool
, canBeRepairedWith :: [Tool] -> Bool -- just to give an example of a function
}
Конечно, вам нужно передать это значение явно, но это может быть полезно, если у вас есть несколько вариантов (например, подумайте Sum
и Product
как можно больше Monoid
для чисел). За исключением того, что у вас более или менее та же выразительность, что и для типа-класса.