谁能向我解释数据/类型构造函数和函数之间有什么区别?Haskell 将它们混合在一起并为我们提供了一个通用接口(所有看起来都像函数,特别是我们可以部分应用它们),而 ML 家族语言则将它们区分开来。
3 回答
您的“Haskell 与 ML 世界”二分法是错误的¹。SML 也将构造函数提升为函数,Caml Light 曾经这样做过。我不确定为什么它在 OCaml 中被删除,我认为设计师认为没有真正需要它。
构造函数有些特殊是有深层原因的。它们可以用于模式,而一般功能不能——我想这可以用极化逻辑和聚焦²来解释。此外,应用于值的构造函数可以被视为一个值,而对于一般函数则不是这样。在按值调用语言中,将递归值定义限制为允许构造函数应用但不允许通用函数应用的子类是很常见的。
我同意将所有构造函数提升为函数的“语义糖”很好。然而,我认为如果我们有一个很好的用于简短抽象的语法糖,例如 Scala 的Some(_)
. 在我看来,是否应该对构造函数进行 curryfied(您的“部分应用程序”评论)是一个不同且正交的问题。
¹:除了是错误的二分法之外,您的问题的语气还带有一定的“Haskellers和MLers,这是戒指,请战斗!”的味道。这可能不是故意的,但无论如何你应该避免这样的表述。语言设计是由妥协组成的,并且假设当两种不同的语言做出不同的选择时,其中一种是正确的,而另一种则不是,这不是一个好方法。
PS:这个问题已经过编辑,现在更加中立。感谢您的编辑。
²:根据petebu的要求,这里有更多(实际上更多)关于“聚焦和极性”的信息。我想指出,我真的不是这个主题的专家(因此是“我猜”)。
我首先推荐这篇论文专注于模式匹配 (PDF)由 Neelakantan Krishnaswami 撰写,2009 年。它包含对不精通极化逻辑和聚焦的人的介绍(但您至少需要熟悉序列微积分)。像往常一样,在随后的微积分中,类型/命题在“右侧”引入,并在“左侧”消除/解构。但是在这里,我们通过“极性”来区分类型,乘积和总和是正数,函数是负数(我的直觉是乘积/总和是数据,而函数是计算,但是这种分离是出于对它们在后续微积分中的行为的非常自然的考虑),并且论文表明箭头类型的左消除对应于函数应用,而 sum/product 类型的左消除对应于模式匹配。有一个case
行为类似于模式匹配(有一些差异)的构造,它消除了正数,因此不能应用于函数。
另一个重要的参考文献是Focusing on Binding and Computation,作者 Dan Licata、Noam Zeilberger 和 Robert Harper,2008 年(请注意,如果您打算提交有关这些主题的论文,“Focusing on ...”正在变得有点陈词滥调)。它不太强调与 ML 风格的模式匹配的联系,而是引入了一个非常好的想法,即计算箭头是“左边的负数”(很容易被视为经典等价A→B ≡ ¬A∨B
),人们可能会引入一个不同的箭头,“左侧正极”,因此是正极化的,可以进行模式匹配。事实证明,这个箭头非常适合表示变量绑定(如果您熟悉高阶抽象语法,这个想法是左极性排除了试图对其变量进行计算的“外来术语”),所以具有变量绑定的术语是可以像求和或乘积一样进行模式匹配的数据结构。
我发现他们的论文有点难以阅读,所以我将从Dan Licata 的幻灯片开始。最后,罗伯特哈珀制作了其他幻灯片,在归纳判断/推导方面提供了不同的直觉:正箭头表示可推导(你能在假设 A 和 B 下建立 C 的推导吗?)而负箭头表示可接受性(给定A 和 B 的推导,你将如何重写/探索/操作它们以建立 C 的推导?)。非常有趣的东西。
首先必须解释值和类型之间的区别。
“因为 Haskell 是一种纯粹的函数式语言,所有计算都是通过评估表达式(句法术语)以产生值(我们认为是答案的抽象实体)来完成的。每个值都有一个关联的类型。” -- Haskell 98 的温和介绍(本教程不是很温和)
Haskell 值是“一流的”,而 Haskell 类型不是。类型用于描述值,值及其类型的关联称为类型。
数据/类型构造函数的区别在于:应用数据构造函数会产生一个值,而应用类型构造函数会产生一个类型。
函数只是描述与类型相关联的值的表达式。当您评估一个函数时,您只是在评估描述值的表达式。
Haskell 确实减少和简化了一些 OCaml 语法。但是 Haskell 语法确实明确地区分了构造函数和函数。函数以小写字母开头,构造函数以大写字母开头。对于中缀运算符,数据构造函数必须以冒号开头,而普通运算符可能永远不会以冒号开头。
构造函数的接口可以看起来像简单的(可能是部分的)函数应用程序,这使得一个参数构造函数和大多数新类型非常易于使用(只是“你好”)。但是 Haskell 确实允许您使用类似 OCaml 的记录名称和 {field=value,field=value} 样式,尽管在 Haskell 中您不必强制使用字段名称或强制使用这种语法。因此 OCaml 仅针对单个字段具有简单的语法,但 Haskell 允许您为多个字段提供简单的语法。最终,避免使用字段名称对于大型类型是不利的,因为位置函数式语法更难重构。