Определение экземпляра 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)?


в какой-то момент вам нужно проверить, равны ли две вещи разного типа. Есть два способа сделать это:

  1. на Typeable класса.
  2. В 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 экземпляра.