16

我有一个很像这样的 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

顺便说一句,这种类型的要点是支持:

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 )且类型相同,则进行相等比较”,但我'不知道我怎么能写出来。

4

2 回答 2

11

我对你的平等非常怀疑,因为它只测试了 F 的一半,但如果这是你真正想要的,那么你可以这样做。请注意,强制转换用作类型相等性的测试,因为如果a内部存在量化的类型相同,则只能比较两个 F。

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

或者这也不是你想要的?再次阅读您的动机,您似乎想要一个 anIn Int可以等于 an的函数In Double

您希望这两者如何比较F floor rF id r(如果rM x :: In Double)?

于 2011-05-17T10:39:46.953 回答
9

在某一时刻,您需要测试两个不同类型的事物是否相等。有两种方法可以做到这一点:

  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实例。

于 2011-05-17T12:20:54.213 回答