Как я могу указать, что две операции коммутируют в классе?
Я начал читать этот документ по CRDT, который является способом совместного использования модифицируемых данных, гарантируя, что операции, которые изменяют данные, являются коммутативными, Мне показалось, что это будет хороший кандидат на абстракцию в Haskell - предоставить класс для CRDT, который задает тип данных и операции, которые коммутируют в этом типе, а затем работать над тем, чтобы библиотека фактически делила обновления между параллельными процессами.
То, что я не могу понять, заключается в том, как заключить контракт, что операции должны коммутироваться в спецификации класса.
Для простого примера:
class Direction a where
turnLeft :: a -> a
turnRight :: a -> a
Нет гарантии, что turnLeft . turnRight
совпадает с turnRight . turnLeft
. Я предполагаю, что резерв должен указать эквивалент законов монады - использовать комментарий для указания ограничений, которые не применяются системой типов.
Ответы
Ответ 1
То, что вы хотите, это класс типа, который включает в себя доказательную нагрузку, что-то вроде псевдо-Haskell:
class Direction a where
turnLeft :: a -> a
turnRight :: a -> a
proofburden (forall a. turnLeft (turnRight a) === turnRight (turnLeft a))
Здесь весь экземпляр должен предоставить обе функции и доказательства для того, чтобы компилятор мог проверить тип. Это желаемое за действительное (для Haskell), поскольку у Haskell нет (ну, ограниченного) понятия доказательства.
OTOH, Coq - помощник доказательства для строго типизированного языка, который можно извлечь в Haskell. Хотя я никогда не использовал классы классов Coq, быстрый поиск является плодотворным, с примером:
Class EqDec (A : Type) := {
eqb : A -> A -> bool ;
eqb_leibniz : forall x y, eqb x y = true -> x = y }.
Таким образом, похоже, что продвинутые языки могут это сделать, но, возможно, многое предстоит сделать для снижения кривой обучения для вашего стандартного разработчика.
Ответ 2
В ответ на TomMD вы можете использовать Agda с тем же эффектом. Хотя он не имеет типов, вы получаете большую часть функциональности (помимо динамической отправки) из записей.
record Direction (a : Set) : Set₁ where
field
turnLeft : a → a
turnRight : a → a
commLaw : ∀ x → turnLeft (turnRight x) ≡ turnRight (turnLeft x)
Я думал, что отредактирую сообщение и ответу на вопрос, почему вы не можете сделать это в Haskell.
В Haskell (+ расширения) вы можете представить эквивалентность, используемую в коде Agda выше.
{-# LANGUAGE GADTs, KindSignatures, TypeOperators #-}
data (:=:) a :: * -> * where
Refl :: a :=: a
Это означает, что теоремы о двух типах равны. Например. a
эквивалентно b
is a :=: b
.
Если мы эквивалентны, мы можем использовать конструктор Refl
. Используя это, мы можем выполнять функции на доказательствах (значениях) теорем (типов).
-- symmetry
sym :: a :=: b -> b :=: a
sym Refl = Refl
-- transitivity
trans :: a :=: b -> b :=: c -> a :=: c
trans Refl Refl = Refl
Все они правильны и, следовательно, истинны. Однако это:
wrong :: a :=: b
wrong = Refl
явно ошибочно и действительно не справляется с проверкой типа.
Однако через все это барьер между значениями и типами не был нарушен. Значения, функции уровня ценности и доказательства все еще живут на одной стороне толстой кишки; типы, функции и теоремы типа на другой. Ваши turnLeft
и turnRight
являются функциями уровня ценности и поэтому не могут участвовать в теоремах.
Agda и Coq зависят языками, где барьер не существует или что-то разрешено пересекать. Strathclyde Haskell Enhancement (SHE) является препроцессором для кода Haskell, который может обмануть некоторые эффекты DTP в Haskell. Он делает это путем дублирования данных из мира ценностей в мире типов. Я не думаю, что он обрабатывает дублирование функций уровня ценности еще, и если бы это произошло, моя догадка, это может быть слишком сложно для обработки.
Ответ 3
Как уже было сказано, нет способа принудительного применения этого непосредственно в Haskell с использованием системы типов. Но если просто указать ограничения в комментариях недостаточно, то в качестве промежуточного уровня вы можете предоставить тесты QuickCheck для желаемых алгебраических свойств.
Что-то в этих строках уже можно найти в пакете шашек; вы можете посоветоваться с ним для вдохновения.
Ответ 4
То, что я не могу понять, заключается в том, как заключить контракт, что операции должны коммутироваться в спецификации класса.
Причина, по которой вы не можете понять, что это невозможно. Вы не можете кодировать этот тип свойств в типах, а не в Haskell по крайней мере.