Определение экземпляра Eq для GADT Haskell
У меня есть GADT, что очень похоже на это:
data In a where
M :: MVar a -> In a
T :: TVar a -> In a
F :: (a -> b) -> In a -> In b
Он обертывает различные входные примитивы, но последний конструктор также допускает экземпляр Functor:
instance Functor In where
fmap f (F g v) = F (f . g) v
fmap f x = F f x
Точка этого типа, BTW, должна поддерживать:
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 необязательно имеют один и тот же тип в этой точке. Я это понимаю. В моем собственном коде я могу долго работать, но похоже, что должен быть способ определить Eq напрямую. На мой взгляд, решение похоже на "продолжайте сверление через конструкторы F до тех пор, пока вы не нажмете M или T, тогда, если они будут одним и тем же конструктором (т.е. как M, так и оба T) и того же типа, выполните сравнение равенства", но я Я не знаю, как я мог это написать.
Ответы
Ответ 1
Я очень подозрительно отношусь к вашему равенству, так как он действительно проверяет только половину 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
есть M x :: In Double
)?
Ответ 2
В какой-то момент вам нужно проверить, равны ли две вещи разного типа. Это можно сделать двумя способами:
- Класс
Typeable
.
- A 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
.