Ваш экземпляр Eq, конечно, выглядит странно для меня.Я бы распутал отмененные пары по два за раз, а не по частям:
instance Eq Ex where
(P s1) == (P s2) = s1 == s2
((:←) e1) == (:←) e2 = e1 == e2
e1 == (:←) ((:←) e2) = e1 == e2
(:←) ((:←) e1) == e2 = e1 == e2
_ == _ = False
Может быть, это эквивалентно тому, что вы написали;это довольно сложно сказать, потому что ваше сопоставление с образцом не очень хорошо согласуется с вашими целями.
Ваш экземпляр Ord также является проблемой, потому что вы не определяете согласованное упорядочение.Для любого набора элементов x y z
, где x < y && y < z
, должно быть, что x < z
.Тем не менее, существуют простые контрпримеры по вашим правилам:
x = P "a"
y = (P "b" :←)
z = ((P "a" :←) :←)
Здесь x == z
несмотря на то, что между ними находится y
.
Один из способов решения обеих проблем - написать simplify
функция, которая удаляет все пары конструкторов отмены и использует ее как в экземплярах Eq
, так и Ord
.Упростите каждый аргумент, чтобы вы знали, что каждый из них имеет либо 0, либо 1 уровень отрицания.Оттуда Eq
легко, и все, что вам нужно сделать, прежде чем вы сможете определить Ord
, это решить, должно ли отрицательное значение быть меньше или больше, чем неотрицательное значение.Вы не можете выбрать, чтобы они были равны, потому что это снова нарушает транзитивность.
Если вы напишете simplify
, вызов по упрощению будет тратиться впустую каждый раз, когда вы касаетесь одного из этих объектов., так как вы сразу выбросите упрощение.Я бы предпочел не экспортировать конструктор для этого типа, а вместо этого предложить умный конструктор, который упрощает перед возвратом значения.Тогда потребители будут знать, что все отрицается один раз или нет вообще.