У меня есть относительно большой набор алгебраических c типов данных, где я не могу автоматически получить Eq
и Ord
, потому что одно поле в типе данных считается метаданными и не должно рассматриваться в равенстве и упорядоченность. Например, тип данных может выглядеть следующим образом:
data Foo = A Int | B String | C String Int | ... | Z String String Int
Где каждый Int в этом случае является метаданными.
Итак, что я делаю, это вручную реализую Eq
, просто сравнивая конструкторы. Но для Ord
это становится безумием, потому что если у меня есть n
конструкторы, я должен реализовать n^2
функции сравнения. Поэтому в настоящее время моя работа заключается в том, чтобы вручную реализовать Hashable
, что требует от меня реализации единственной функции ha sh для каждого конструктора. А затем просто выполните сравнение ha sh в моем экземпляре Ord
.
Очевидно, что здесь есть некоторые проблемы, поскольку compare (hash x) (hash y) == EQ -> x == y
не выполняется, поскольку два разных значения могут иметь одинаковое значение ha sh. Однако это можно сделать, сначала проверив равенство вручную, и в этом случае всегда говорите, что левая сторона меньше, чем правая.
Однако теперь у вас есть это для некоторых значений любого типа: a < b && b < a
. Но я не уверен, что это разрешено в экземпляре Haskell Ord
. Так что, в основном, мой вопрос, является ли это Оке для реализации Ord, как это или нет? Мне нужна Ord
, потому что многие библиотеки требуют Ord
. Например, библиотеки графов и библиотеки карт.
Вот полный пример:
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Test where
import Prelude
import Data.Bits (xor)
import Data.Hashable (Hashable (..))
data Foo = A Int | B String | C String Int | Z String String Int
instance Eq Foo where
(A _) == (A _) = True
(B x1) == (B x2) = x1 == x2
(C x1 _) == (C x2 _) = x1 == x2
(Z x1 y1 _) == (Z x2 y2 _) = x1 == x2 && y1 == y2
_ == _ = False
instance Hashable Foo where
hashWithSalt s (A _) = s `xor` (hash @Int 1)
hashWithSalt s (B x) = s `xor` (hash @Int 2) `xor` (hash x)
hashWithSalt s (C x _) = s `xor` (hash @Int 3) `xor` (hash x)
hashWithSalt s (Z x y _) = s `xor` (hash @Int 4) `xor` (hash x) `xor` (hash y)
instance Ord Foo where
compare (hash -> a) (hash -> b) = case compare a b of
EQ -> if a == b then EQ else LT
e -> e