Определение экземпляра Eq для Haskell GADTs
у меня есть ГАДТ, который очень похож на это:
data In a where
M :: MVar a -> In a
T :: TVar a -> In a
F :: (a -> b) -> In a -> In b
он обертывает различные входные примитивы, но последний конструктор также допускает экземпляр функтора:
instance Functor In where
fmap f (F g v) = F (f . g) v
fmap f x = F f x
точка этого типа, кстати, заключается в поддержке:
read :: In a -> IO a
read (M v) = takeMVar v
read (T v) = atomically (readTVar v)
read (F f v) = f <$> read v
то, что я хочу иметь возможность сделать, это определить очевидный экземпляр Eq для этого типа, что-то вроде:
instance Eq (In a) where
(M x) == (M y) = x == y
(T x) == (T y) = x == y
(F _ x) == (F _ y) = x == y
_ == _ = False
проблема в третьем случае, который терпит неудачу, потому что x и y не обязательно имеют один и тот же тип точка. Я это понимаю. В моем собственном коде я могу сделать длинный обходной путь, но кажется, что должен быть способ напрямую определить эквалайзер. На мой взгляд, решение-это что-то вроде "продолжайте сверлить конструкторы F, пока не нажмете M или T, тогда, если они один и тот же конструктор (т. е. оба M или оба T) и один и тот же тип, сделайте сравнение равенства", но я не уверен, как я мог бы это написать.
2 ответов
Я очень подозрительно отношусь к вашему равенству, так как оно действительно проверяет только половину F, но если это то, что вы действительно хотите, вот как вы можете это сделать. Обратите внимание, что приведение служит тестом для равенства типов, так как вы можете сравнить только два Fs, если типы экзистенциально количественно a
внутри то же самое.
data In a where
M :: MVar a -> In a
T :: TVar a -> In a
F :: (Typeable a) => (a -> b) -> In a -> In b
deriving (Typeable)
instance Eq (In a) where
(M x) == (M y) = x == y
(T x) == (T y) = x == y
(F _ x) == (F _ y) = Just x == cast y
_ == _ = False
или, может быть, это не то, что вы хотите либо? Чтение вашей мотивации снова кажется, что вы хотите функцию, где In Int
может быть равным In Double
.
как бы вы хотели, чтобы эти два сравнить F floor r
и F id r
(если r
is M x :: In Double
)?
в какой-то момент вам нужно проверить, равны ли две вещи разного типа. Есть два способа сделать это:
- на
Typeable
класса. - В GADT
data Equal a b where Eq :: Equal a a
.
С MVar
и TVar
не поддерживает 2, вам придется использовать Typeable
класса. Другими словами, вам придется увеличить свой тип данных с помощью Typeable
ограничения.
к счастью, у вас есть некоторая свобода относительно того, где ставить ограничения. Для например, вы можете поместить их следующим образом:
data In a where
M :: Typeable a => MVar a -> In a
T :: Typeable a => TVar a -> In a
F :: (a -> b) -> In a -> In b
equal :: In a -> In b -> Bool
equal (M x) (M y) = Just x == cast y
equal (T x) (T y) = Just x == cast y
equal (F _ x) (F _ y) = x `equal` y
equal _ _ = False
instance Eq (In a) where
(==) = equal
таким образом, вы получите сохранить Functor
экземпляра.