【发布时间】:2013-10-11 05:02:25
【问题描述】:
最近出现了一个关于 Sets 的讨论,它在 Scala 中支持 zip 方法以及这如何导致错误,例如
scala> val words = Set("one", "two", "three")
scala> words zip (words map (_.length))
res1: Set[(java.lang.String, Int)] = Set((one,3), (two,5))
我认为很明显Sets 不应该支持zip 操作,因为元素没有排序。但是,有人建议问题在于Set 并不是真正的函子,也不应该有map 方法。当然,你可以通过映射一个集合给自己带来麻烦。现在切换到 Haskell,
data AlwaysEqual a = Wrap { unWrap :: a }
instance Eq (AlwaysEqual a) where
_ == _ = True
instance Ord (AlwaysEqual a) where
compare _ _ = EQ
现在在 ghci 中
ghci> import Data.Set as Set
ghci> let nums = Set.fromList [1, 2, 3]
ghci> Set.map unWrap $ Set.map Wrap $ nums
fromList [3]
ghci> Set.map (unWrap . Wrap) nums
fromList [1, 2, 3]
所以Set 不满足函子定律
fmap f . fmap g = fmap (f . g)
可以说这不是maps 上map 操作的失败,而是我们定义的Eq 实例的失败,因为它不遵守替换法则,即对于 A 和 B 上的两个 Eq 实例和一个映射 f : A -> B 然后
if x == y (on A) then f x == f y (on B)
不适用于AlwaysEqual(例如考虑f = unWrap)。
对于Eq 类型,我们应该尽量尊重替代法吗?当然,我们的AlwaysEqual 类型尊重其他平等法则(对称性、传递性和自反性得到微不足道的满足),所以替换是我们唯一可能遇到麻烦的地方。
对我来说,substition 似乎是Eq 类的一个非常理想的属性。另一方面,recent Reddit discussion 上的一些 cmets 包括
“替换似乎比必要的要强,基本上是对类型进行商化,对使用该类型的每个函数提出要求。”
-- godofpumpkins
“我也真的不想要替换/全等,因为我们想要等同但在某种程度上可以区分的值有许多合法用途。”
-- sclv
“替换仅适用于结构平等,但没有任何东西坚持
Eq是结构性的。”-- edwardkmett
这三个在 Haskell 社区中都很有名,所以我会犹豫反对他们并坚持我的 Eq 类型的可替代性!
另一个反对 Set 是 Functor 的论点 - 人们普遍认为 Functor 允许您在保留形状的同时转换“集合”的“元素”。例如,Haskell wiki 上的这句话(注意 Traversable 是 Functor 的概括)
“
Foldable使您能够通过结构处理元素但丢弃形状,Traversable允许您在保留形状的同时做到这一点,例如,放入新值。”“
Traversable是关于保持原样的结构。”
在现实世界中的 Haskell
"...[A] 函子必须保持形状。集合的结构不应受到函子的影响;只有它包含的值应该改变。"
显然,Set 的任何仿函数实例都有可能通过减少集合中的元素数量来改变形状。
但似乎Sets 真的应该是函子(暂时忽略Ord 的要求 - 我认为这是我们希望有效使用集合的人为限制,而不是任何绝对要求例如,函数集是一个非常明智的考虑。无论如何,Oleg has shown 如何为不需要 Ord 约束的 Set 编写高效的 Functor 和 Monad 实例。它们的用途太多了(不存在的Monad 实例也是如此)。
谁能收拾这个烂摊子? Set 应该是 Functor 吗?如果是这样,如何处理违反函子定律的可能性? Eq 的法则应该是什么,它们如何与 Functor 和 Set 实例的法则相互作用?
【问题讨论】:
-
也许我错了,但我不认为 Scala 中的集合是函子,实际上 Scala 中没有函子、单子、应用程序和标准中的其他东西图书馆,原因教授。奥德斯基不想拥有它们。也许在 Haskell 中它对语言来说是必不可少的,但在 Scala 中,我认为 Set 只是一个 Set,如果你需要 Functors、Monads 等。使用 Scalaz
-
@levy 而不是“函子”,您可以只阅读“支持
map操作的类型集”。这就是Functor的全部内容(好吧,再加上一些法律)。 -
@ChrisTaylor 我同意你的观点,但是 Scala 中的 map 和 Haskell 中的 map 背后的想法是不同的。在 Haskell 中,map 与 Functors、Arrows 和所有来自 CatThreory 的数学东西有关,但在 Scala 中,map 只是“将这个
addOne函数应用到每个Int”。我怀疑 Scala Devs 的最大部分在 Monads 方面考虑flatMap/bind以及对带有上下文的计算流的一些抽象,它只是调用函数并从List[List[A]]生成List[A]。 -
Set的map不是Functor映射! -
我认为我们可以说
Set是子类别上的函子,其对象是具有健全Eq/Ord实例的类型(这里的“健全”包括可替代性)。
标签: scala haskell equality functor