Set
不是仿函数的原因是here。似乎归结为a == b && f a /= f b
是可能的事实。那么,为什么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
的实例?“。
另外,我的实例声明是垃圾(感谢@n.m。)。我应该说:
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)
答案 0 :(得分:14)
instance StrongEq a => Functor (Set a) where
无论是StrongEq
意味着什么,这在Haskell和大数学/分类方案中都没有意义。
在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
是准确的)是一个算子,没有问题。
答案 1 :(得分:6)
由于我不是一个类别理论家,我会尝试写一个更具体/实用的解释(即我能理解的):
关键点是@leftaroundabout在评论中提出的那个:
==
应该是 证人“相当于所有可观察的手段”(这不一定 requirea == 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 Leaf
和Node 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;博士:制作Eq
的实例是一种向世界发表强烈声明的方式;如果不理解,请不要发表声明。
答案 2 :(得分:3)
您的第二个Functor
实例也没有任何意义。 Set
在Haskell中Functor
不能是fmap
的最大原因是StrongEq
不能有约束。将不同的相等概念发明为fmap
并不会改变您无法在Set实例中的fmap
上编写这些约束的事实。
fmap
一般不应该拥有您需要的约束。例如,设置函数的函子非常有意义(没有使用Applicative在函数内部应用函数的整个概念),函数通常不能是Eq或StrongEq的成员。
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)
只能对某些实例有额外的限制,因为代码如下:
f
此代码声称无论函子g
和h
如何都可以使用,无论函数j
和fmap
如何。它无法检查其中一个仿函数是否是对fmap :: (a -> b) -> Set a -> Set b
有额外约束的特殊仿函数,也无法检查其应用的某个函数是否会违反这些约束。
说Set是Haskell中的一个Functor,就是说有一个(合法的)操作Functor
,具有那种确切的类型。这是正是 fmap :: (Eq a -> Eq b) => (a -> b) -> Set a -> Set b
的含义。 Ord
不是此类操作的示例。
我可以理解,可以使用ConstraintKinds GHC extendsion编写一个不同的 Functor类,该类允许对由Functor改变的值进行约束(您实际需要的是{ {1}}约束,而不仅仅是Eq
)。 This blog post讨论如何创建一个可以拥有Set实例的新Monad类。我从来没有玩过像这样的代码,所以我不知道该技术是否存在。它无法帮助您将集合移交给需要Functors的现有代码,但如果您愿意,您应该可以在自己的代码中使用它而不是Functor。
答案 3 :(得分:2)
StrongEq
这个概念很难。一般而言,平等是一个计算机科学变得比典型数学更严格的地方,这种方式使得事情具有挑战性。
特别是,典型的数学喜欢谈论对象,好像它们存在于集合中并且可以被唯一地识别。计算机程序通常处理并不总是可计算的类型(作为一个简单的反例,告诉我对应于data U = U (U -> U)
类型的集合是什么)。这意味着关于两个值是否可识别可能是不可判定的。
这在依赖类型语言中成为一个巨大的主题,因为类型检查需要识别类似的类型,并且依赖类型的语言可能在其类型中具有任意值,因此需要一种方法来预测相等。
因此,StrongEq
可以在Haskell的受限制部分上定义,只包含可以对其进行可判定比较的类型。我们可以将这个类别视为可计算函数的箭头,然后将Set
视为从类型到该类型值集类型的endofunctor。不幸的是,这些限制使我们远离标准的Haskell,并使StrongEq
或Functor (Set a)
的定义略微不实用。