Что произойдет с вами, если вы нарушите законы монады?
Сделайте компилятор или более "родные" части библиотек (IO или функции, которые имеют доступ к черной магии и реализации) делают предположения об этих законах? Нарушение их приведет к невозможному?
Или они просто выражают шаблон программирования - т.е. единственный человек, которого вы будете раздражать, нарушая их, - это люди, которые используют ваш код, и не ожидали, что вы так небрежны?
Ответы
Ответ 1
Компилятор не делает никаких предположений о законах, однако, если ваш экземпляр не подчиняется законам, он не будет вести себя как монада - он будет делать странные вещи и в противном случае будет казаться, что ваши пользователи не работают правильно (например, сброс значений или оценка вещей в неправильном порядке).
Кроме того, рефакторинг, который могут сделать ваши пользователи, считая, что удержание монадов удержания, очевидно, не будет звуковым.
Ответ 2
Законы монады - это просто дополнительные правила, за которыми, как ожидается, будут следовать экземпляры, помимо того, что может быть выражено в системе типов. Поскольку Monad
выражает шаблон программирования, законы являются частью этого шаблона. Такие законы применимы и к другим типам классов: Monoid
имеет очень похожие правила для Monad
, и обычно ожидалось, что примеры Eq
будут следовать правилам, ожидаемым для отношения равенства, среди других примеров.
Поскольку эти законы в некотором смысле являются "частью" класса типа, для другого кода должно быть разумным ожидать, что они будут удерживаться и действовать соответствующим образом. Таким образом, несовместимые экземпляры могут нарушать предположения, сделанные логикой клиентского кода, что приводит к ошибкам, вину за которые правильно помещают в экземпляр, а не код, использующий его.
Короче говоря, "нарушение законов монады" обычно следует читать как "писать багги-код".
Я проиллюстрирую этот момент примером, включающим другой тип класса, измененный от одного, заданного Даниэлем Фишером в списке рассылки haskell-cafe. Хорошо известно, что стандартные библиотеки включают в себя некоторые ошибочные экземпляры, а именно Eq
и Ord
для типов с плавающей запятой. Неправильное поведение происходит, как вы могли догадаться, когда участвует NaN. Рассмотрим следующую структуру данных:
> let x = fromList [0, -1, 0/0, -5, -6, -3] :: Set Float
Где 0/0
создает NaN, что нарушает предположения о Ord
экземплярах, сделанных Data.Set.Set
. Этот Set
содержит 0
?
> member 0 x
True
Да, конечно, так оно и есть прямо на виду! Теперь мы вставляем значение в Set
:
> let x' = insert (0/0) x
Этот Set
по-прежнему содержит 0
, правильно? В конце концов, мы ничего не удаляли.
> member 0 x'
False
... о. О, дорогая.
Ответ 3
Для людей, работающих на более "основных" языках, это будет похоже на реализацию интерфейса, но это делается неправильно. Например, представьте, что вы используете фреймворк, который предлагает интерфейс IShape, и вы его реализуете. Однако ваша реализация метода draw() вообще не рисуется, а вместо этого просто создает экземпляр еще 1000 экземпляров вашего класса.
Структура попытается использовать ваш IShape и делать с ним разумные вещи, и Бог знает, что произойдет. Было бы любопытно наблюдать за крутой поездкой.
Если вы говорите, что вы Монада, вы "заявляете", что придерживаетесь его контракта и законов. Другой код будет считать вашу декларацию и действовать соответственно. Поскольку вы солгали, все пойдет не так непредвиденным образом.