Прежде всего, нам видно, что экземпляры класса >Monoid
могут быть определены только для конкретных типов, потому что идентификатор >m
в определении класса типов не принимает никаких параметров типа. В этом состоит отличие от классов >Functor
и >Applicative
, которые требуют, чтобы их экземплярами были конструкторы типа, принимающие один параметр.
Первой функцией является >mempty
. На самом деле это не функция, поскольку она не принимает параметров. Это полиморфная константа вроде >minBound
из класса >Bounded
. Значение >mempty
представляет единицу для конкретного моноида.
Далее, у нас есть функция >mappend
, которая, как вы уже, наверное, догадались, является бинарной. Она принимает два значения одного типа и возвращает ещё одно значение того же самого типа. Решение назвать так функцию >mappend
было отчасти неудачным, поскольку это подразумевает, что мы в некотором роде присоединяем два значения. Тогда как оператор >++
действительно принимает два списка и присоединяет один в конец другого, оператор >*
на самом деле не делает какого-либо присоединения – два числа просто перемножаются. Когда вы встретите другие экземпляры класса >Monoid
, вы поймёте, что большинство из них тоже не присоединяют значения. Поэтому избегайте мыслить в терминах присоединения; просто рассматривайте >mappend
как бинарную функцию, которая принимает два моноидных значения и возвращает третье.
Последней функцией в определении этого класса типов является >mconcat
. Она принимает список моноидных значений и сокращает их до одного значения, применяя функцию >mappend
между элементами списка. Она имеет реализацию по умолчанию, которая просто принимает значение >mempty
в качестве начального и сворачивает список справа с помощью функции >mappend
. Поскольку реализация по умолчанию хорошо подходит для большинства экземпляров, мы не будем сильно переживать по поводу функции >mconcat
. Когда для какого-либо типа определяют экземпляр класса >Monoid
, достаточно реализовать всего лишь методы >mempty
и >mappend
. Хотя для некоторых экземпляров функцию >mconcat
можно реализовать более эффективно, в большинстве случаев реализация по умолчанию подходит идеально.
Прежде чем перейти к более конкретным экземплярам класса >Monoid
, давайте кратко рассмотрим законы моноидов.
Вы узнали, что должно иметься значение, которое действует как тождество по отношению к бинарной функции, и что бинарная функция должна быть ассоциативна. Можно создать экземпляры класса >Monoid
, которые не следуют этим правилам, но такие экземпляры никому не нужны, поскольку, когда мы используем класс типов