Раздел 21 · Haskell

Semigroup и Monoid: склейка с законами

middle~40 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Semigroup и Monoid: склейка с законами

Первая ступень иерархии абстракций. Semigroup это один оператор склейки и один закон, Monoid добавляет нейтральный элемент и ещё два закона. Звучит как мелочь, но именно эта пара объясняет, почему <> работает и на строках, и на числах, и на настройках, и почему foldMap собирает агрегат из чего угодно. В этом уроке: законы на пальцах, зачем нужны newtype Sum и Product, mconcat и foldMap, моноиды в конфигурациях и агрегациях, инстансы своими руками.

Идея

В уроке про свёртки ты заметил две вещи про операторы. Первая: бывают ассоциативные операторы, у которых расстановка скобок не меняет ответ. Вторая: у многих из них есть нейтральное стартовое значение, ноль для сложения, единица для умножения, пустой список для конкатенации. Тогда я обещал, что у этой пары свойств есть имена. Имена вот они.

Ассоциативный оператор склейки это Semigroup. Один метод, один закон:

class Semigroup a where
  (<>) :: a -> a -> a

-- закон ассоциативности:
-- (x <> y) <> z  ==  x <> (y <> z)

Читается <> как mappend или просто склейка. Закон не проверяется компилятором: это контракт, который ты обязан соблюсти руками, иначе всё, что построено поверх Semigroup, начнёт врать. Ровно как законы Eq и Ord из прошлого урока.

Monoid это Semigroup, у которого есть нейтральный элемент:

class Semigroup a => Monoid a where
  mempty :: a

-- законы тождества:
-- mempty <> x  ==  x      (левый)
-- x <> mempty  ==  x      (правый)

mempty это значение, которое ничего не делает при склейке. Заголовок Semigroup a => Monoid a говорит то же, что Eq a => Ord a в прошлом уроке: чтобы стать Monoid, тип сначала обязан быть Semigroup. Иерархия выстраивается снизу вверх, и это первый её этаж.

Самый честный пример это список:

instance Semigroup [a] where
  (<>) = (++)

instance Monoid [a] where
  mempty = []

Склейка списков ассоциативна, пустой список ничего не приклеивает. Проверь в ghci:

ghci> [1, 2] <> [3, 4]
[1,2,3,4]
ghci> "при" <> "вет"
"привет"
ghci> mempty <> [1, 2, 3]
[1,2,3]

Строка это [Char], поэтому конкатенация строк это тот же инстанс. Один интерфейс, и он уже покрыл два разных типа. Дальше начинается интересное.

премиум-урок

Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.

Оплата пока не подключена: премиум и курсы открываются промокодом.