gpt4 book ai didi

haskell - 平等类(Class)的误会

转载 作者:行者123 更新时间:2023-12-02 14:02:40 25 4
gpt4 key购买 nike

  • 我有自己的数据类型来表示图的节点和边,如下所示:

    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 aNode 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

我们仍然可以派生 EqShow 实例,但语法略有不同:

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

现在我们可以取消 isEdgeEq 约束,因为 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com