- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有自己的数据类型来表示图的节点和边,如下所示:
data Node a = Node a deriving (Show, Eq)
data Label a = Label a deriving (Show)
data Cost = CostI Int | CostF Float deriving (Show)
data Edge label node = Edge (Label label, (Node node,Node node), Cost) deriving (Show)
现在,我创建一个函数来检查一条边是否包含 2 个节点,如下所示:
isEdge:: (Eq n) => (Edge l n) -> (Node n, Node n) -> Bool
isEdge (Edge (_, (n1,n2), _)) (n3, n4) = result
where result = (n1 == n3) && (n2 == n4)
该函数运行良好,这里的问题是如果我从函数中删除 (Eq n),它就会失败。那么,为什么会这样,即使在上面的声明中我声明 Node
派生自 Eq 类?
data Node a = Node a deriving (Show, Eq)
最佳答案
GHC 为 Node a
派生的 Eq
实例如下所示:
instance Eq a => Eq (Node a) where
(Node x) == (Node y) = x == y
(Node x) /= (Node y) = x /= y
可以使用-ddump-deriv
编译来查看生成的代码。出于显而易见的原因,需要 Eq a
约束。因此,GHC 无法推断 Node (a -> b)
的 Eq
实例,因为无法比较函数。
但是,事实上 GHC 无法为 some a
的 Node a
推断出 Eq
的实例> 并不意味着它会阻止您构造 Node a
类型的值,其中 a
不是相等类型。
如果您想阻止人们构建不可比较的Node
,您可以尝试设置如下约束:
data Eq a => Node a = Node a deriving (Eq, Show)
但是现在 GHC 告诉我们需要一个编译器编译指示:
Illegal datatype context (use -XDatatypeContexts): Eq a =>
好的,让我们将其添加到文件的顶部:
{-# LANGUAGE DatatypeContexts #-}
现在编译:
/tmp/foo.hs:1:41: Warning: -XDatatypeContexts is deprecated: It was widely
considered a misfeature, and has been removed from the Haskell language.
问题是,现在使用 Node
的每个函数都需要一个 Eq
类约束,这很烦人(你的函数仍然需要约束!)。 (此外,如果您的用户想要使用非相等类型创建 Node,但从未测试它们是否相等,那么问题是什么?)
但是,实际上有一种方法可以让 GHC 执行您想要的操作:广义代数数据类型 ( GADTs ):
{-# LANGUAGE GADTs, StandaloneDeriving #-}
data Node a where
Node :: Eq a => a -> Node a
这看起来就像你原来的定义,除了它强调 Node
值构造函数(以前位于数据声明右侧的那个)只是一个函数,您可以向其添加约束。现在 GHC 知道只有相等类型可以放入 Node 中,并且与我们之前尝试的解决方案不同,我们可以创建不需要约束的新函数:
fromNode :: Node a -> a
fromNode (Node x) = x
我们仍然可以派生 Eq
和 Show
实例,但语法略有不同:
deriving instance Eq (Node a)
deriving instance Show (Node a)
(因此出现了上面的 StandaloneDeriving 编译指示。)
为此,GHC 还要求我们向 GADT 添加 Show
约束(如果您再次查看生成的代码,您会发现约束现在消失了):
data Node a where
Node :: (Eq a, Show a) => a -> Node a
现在我们可以取消 isEdge
的 Eq
约束,因为 GHC 可以推断它!
(对于这样一个简单的情况来说,这绝对是矫枉过正了——再说一遍,如果人们想要构造带有内部函数的节点,为什么不应该呢?但是,当您想要强制执行某些类似的情况时,GADT 非常有用。数据类型的属性。请参阅 a cool example )。
<小时/>编辑(从 future ):你也可以写
data Node a = (Eq a, Show a) => Node a
但您仍然需要单独启用 GADT 扩展和派生实例。请参阅this thread .
关于haskell - 平等类(Class)的误会,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13506112/
我是一名优秀的程序员,十分优秀!