为什么Haskell没有比Eq更强大的替代方案?

这里给出了Set不是函子的原因。 这似乎归结为a == b && fa /= fb是可能的事实。 那么,为什么Haskell没有像Eq那样的标准替代方案?

class Eq a => StrongEq a where
    (===) :: a -> a -> Bool
    (/==) :: a -> a -> Bool
    x /== y = not (x === y)
    x === y = not (x /== y)

对于这些情况应该遵守法律

∀a,b,f. not (a === b) || (f a === f b)
∀a. a === a
∀a,b. (a === b) == (b === a)

也许还有其他一些? 然后我们可以有:

instance StrongEq a => Functor (Set a) where
    -- ...

或者我错过了什么?


编辑 :我的问题不是“为什么没有Eq实例的类型?”,就像你似乎已经回答了一些。 恰恰相反:“为什么Eq实例不是延伸平等的? 为什么有太多的Eq实例?“,加上”如果a == b确实意味着延伸平等,为什么Set不是Functor的实例?“。

另外,我的实例声明是垃圾(谢谢@ nm)。 我应该说:

newtype StrongSet a = StrongSet (Set a)
instance Functor StrongSet where
    fmap :: (StrongEq a, StrongEq b) => (a -> b) -> StrongSet a -> StrongSet b
    fmap (StrongSet s) = StrongSet (map s)

你的第二个Functor实例也没有任何意义。 Set不能成为Haskell中的Functor的最大原因是fmap不能有约束。 发明不同的平等概念,因为StrongEq不会改变您无法在Set实例fmap这些约束写入fmap的事实。

fmap不应该有你需要的限制。 例如,有函数的函数是非常有意义的(例如,没有它使用Applicative在函数内部应用函数的整个概念失效),函数不能是Eq或StrongEq的成员。

由于代码如下所示, fmap不能只对某些实例有额外的约束:

fmapBoth :: (Functor f, Functor g) => (a -> b, c -> d) -> (f a, g c) -> (f b, g d)
fmapBoth (h, j) (x, y) = (fmap h x, fmap j y)

无论函子fg如何,以及函数hj如何,该代码都声称可以工作。 它无法检查其中一个函数是否是一个对fmap有额外约束的特殊fmap ,也没有办法检查它所应用的函数是否违反了这些约束。

说Haskell是Haskell中的一个Functor,它说有一个(合法的)操作fmap :: (a -> b) -> Set a -> Set b ,这个确切的类型。 这正是 Functor意思。 fmap :: (Eq a -> Eq b) => (a -> b) -> Set a -> Set b不是这样一个操作的例子。

据我所知,使用ConstraintKinds GHC extendsion编写一个不同的Functor类是可能的,该类允许根据Functor而变化的值进行约束(并且您实际需要的是Ord约束,而不仅仅是Eq )。 这篇博文讲述了如何创建一个新的Monad类,该类可以有一个Set实例。 我从来没有玩过这样的代码,所以我不知道这个技术的存在。 它不会帮助您将Sets移交给需要Functors的现有代码,但如果您愿意,您应该可以在自己的代码中使用它而不是Functor。


instance StrongEq a => Functor (Set a) where

无论StrongEq含义如何,这在Haskell和事物的宏伟数学/分类方案中都是StrongEq

在Haskell中, Functor需要类型* -> *的类型构造函数。 箭头反映了在类别理论中,函数是一种映射。 []和(假设的) Set是这样的类型构造函数。 [a]Set a善良*而不能是仿函数。

在Haskell中,很难定义Set ,因此它可以变成Functor因为无论如何,对于某些类型来说,不能明确定义平等。 例如,您无法比较Integer->Integer类型的两个东西。

假设有一个函数

goedel :: Integer -> Integer -> Integer
goedel x y = -- compute the result of a function with 
             -- Goedel number x, applied to y

假设你有一个值s :: Set Integer 。 什么fmap goedel s应该看起来像? 你如何消除重复?

在你的典型集合论中,平等是神奇地为所有事物定义的,包括函数,所以Set (或Powerset是精确的)是一个函子,没有问题。


既然我不是范畴理论家,我会尝试写一个更具体/实用的解释(即我能理解的一个):

关键点是@leftaroundabout在评论中所做的一点:

==应该见证所有可观察手段的“等价物”(这不一定需要a == b必须只适用于相同的实现;但任何你可以“正式”用a和b做的事情都应该得到相同的结果。永远不应该暴露unAlwaysEq )。 如果你不能确保某种类型,你不应该给它一个Eq实例。

也就是说,你的StrongEq应该没有必要,因为这就是Eq应该已经有的。

Haskell的价值往往是为了表现某种数学或“现实生活”的价值。 很多时候,这种表示是一对一的。 例如,考虑类型

data PlatonicSolid = Tetrahedron | Cube |
   Octahedron | Dodecahedron | Icosahedron

这种类型只包含每个柏拉图立体的一种表示形式。 我们可以通过在声明中添加deriving Eq来利用它,并且它会生成正确的实例。

但是,在很多情况下,同一个抽象值可能由多个Haskell值表示。 例如,红黑树Node B (Node R Leaf 1 Leaf) 2 LeafNode B Leaf 1 (Node R Leaf 2 Leaf)都可以表示集{1,2}。 如果我们在我们的声明中添加deriving Eq ,我们将得到一个Eq的实例,区分我们想要被认为是相同的事情(在集合操作的实现之外)。

确保类型只在适当情况下才能生成Eq (和Ord )的实例很重要! 把Ord某个实例放在一个需要排序的数据结构中是很有诱惑力的,但是如果排序并不是真正的抽象值的总排序,那么所有的破坏方式都会随之而来。 例如,除非文档绝对保证它,否则称为sort :: Ord a => [a] -> [a]的函数不仅可能是一种不稳定的排序,甚至可能不会生成包含所有Haskell值的列表进去。 sort [Bad 1 "Bob", Bad 1 "James"]可合理制作[Bad 1 "Bob", Bad 1 "James"][Bad 1 "James", Bad 1 "Bob"][Bad 1 "James", Bad 1 "James"][Bad 1 "Bob", Bad 1 "Bob"] 。 所有这些都是完全合法的。 一个函数使用后台空间中的unsafePerformIO来实现拉斯维加斯风格的随机算法,或者通过线程相互竞争来获得最快的答案,甚至可以在不同时间给出不同的结果,只要它们分别是==其他。

tl; dr:使某件事情成为Eq一个实例,是向全世界发出强烈声明的一种方式; 如果你不是这个意思的话,不要这样说。

链接地址: http://www.djcxy.com/p/43333.html

上一篇: Why doesn't Haskell have a stronger alternative to Eq?

下一篇: Why Functor class has no return function?