数据/类型构造器和函数之间的区别是什么?

9
有人能解释一下数据/类型构造函数和函数之间的区别吗? Haskell将它们混合在一起并给我们提供了一个通用接口(所有内容看起来都像函数,特别是我们可以部分应用它们),而ML系列语言则区分它们。

1
Haskell 没有“一切皆为函数”的座右铭!http://conal.net/blog/posts/everything-is-a-function-in-haskell/ - sclv
3个回答

19
你的二分法“Haskell vs. the ML world”是错误的¹。SML也将构造函数提升为函数,Caml Light也曾如此。我不确定为什么它在OCaml中被移除了,我认为设计者认为没有真正的需要。
有深刻的原因使得构造函数略有不同。它们可以用于模式匹配,而一般的函数不能——我想这可以用极化逻辑和聚焦²来解释。此外,应用于值的构造函数可以被视为值,而对于一般函数则不成立。在按值调用的语言中,通常会将递归值定义限制为允许构造函数应用,但不允许一般函数应用的子类。
我同意将所有构造函数提升为函数的“语义糖”很好。然而,如果我们有一个像Scala的Some(_)这样的简短抽象的良好语法糖,那么这将不是那么有用。 构造函数是否应该被柯里化(您的“部分应用”备注)是一个不同且正交的问题,在我看来。
¹:除了是错误的二分法之外,你的问题口气有点像“Haskellers和MLers,这是拳击比赛场地,请打架!”。这可能不是有意的,但无论如何,你应该避免这样的表述。语言设计是由妥协构成的,假设当两种不同的语言做出不同选择时,其中一种是“正确”的,而另一种不是,这并不是一个好的方法。 附:问题已经被编辑,现在更为中立。感谢您的编辑。




²:根据petebu的要求,这里提供更多(实际上是很多)关于“聚焦和极性”的信息。我想指出,我并不是这个主题的专家(因此是“我猜”)。

我首先推荐阅读Neelakantan Krishnaswami在2009年发表的论文Focusing on Pattern Matching (PDF)。它为那些不精通极化逻辑和聚焦的人提供了介绍(但您至少需要熟悉序列演算)。与序列演算中一样,类型/命题被引入到“右边”,并且被消除/解构到“左边”。但是,在这里,我们通过“极性”来区分类型,积和和为正,函数为负(我的直觉是积/和是数据,而函数是计算,但是这种分离是由它们在序列演算中的行为的非常自然的考虑所驱动的),论文表明箭头类型的左消除对应于函数应用,而和/积类型的左消除对应于模式匹配。有一个case结构,其行为类似于模式匹配(有些差异),它消除了正数,因此不能应用于函数。

另一个重要的参考资料是2008年Dan Licata、Noam Zeilberger和Robert Harper的关注绑定和计算(请注意,如果您计划提交有关这些主题的论文,“关注...”已经有点陈词滥调)。它强调了与ML风格模式匹配的联系要少得多,但介绍了非常好的想法,即虽然计算箭头在左侧是“负面的”(很容易看出是经典等价A→B ≡ ¬A∨B),但可以引入不同的箭头,在左侧是“正面的”,因此是正极化的,可以进行模式匹配。结果发现,这个箭头非常适合表示变量绑定(如果您熟悉高阶抽象语法,那么左极性规则排除了试图在其变量上计算的“异类术语”),因此具有变量绑定的术语是可以像总和或乘积一样进行模式匹配的数据结构。
我发现他们的论文有点难以理解,所以我会从Dan Licata的幻灯片开始。最后,Robert Harper制作了其他幻灯片,提供了不同的归纳判断/推导直觉:正箭头表示可导性(在假设A和B的情况下,您能否建立C的推导?),而负箭头表示可允许性(给定A和B的推导,您将如何重写/探索/操作它们以建立C的推导?)。非常有趣的东西。

“这可以用极化逻辑和聚焦来解释。”你能详细说明一下吗?提供一篇论文链接也可以。 - petebu

1

首先需要解释值和类型之间的区别。

"因为 Haskell 是一种纯函数式语言,所有计算都是通过求值表达式(语法术语)来产生值(我们认为是答案的抽象实体)。每个值都有一个关联的类型。" -- 《Haskell 98 温柔入门》(这个教程并不十分温柔)

Haskell 值是“一等公民”,而 Haskell 类型不是。类型用于描述值,将值与其类型关联起来称为类型化。

数据/类型构造器之间的区别在于:应用数据构造器会产生一个值,但应用类型构造器会产生一个类型。

函数只是描述值的表达式,与一个类型相关联。当你评估一个函数时,你只是在评估描述值的表达式。


我相信OP是在询问代数数据类型构造函数和函数之间的区别,两者都产生值。 - petebu
1
@petebu 我曾经也有同样的看法,但实际上 OP 的问题非常模糊,他的问题可以(在阅读了这个答案之后我意识到)被理解为“为什么 ML 不像 Haskell 一样在类型层面上使用类似函数的语法?”我想最自然的答案是,ML 在模块语言之外不支持高阶类型,因此部分应用是没有意义的。 - gasche

0

Haskell 确实简化了一些 OCaml 语法。但是 Haskell 语法可以明确区分构造函数和普通函数。函数以小写字母开头,构造函数以大写字母开头。对于中缀运算符,数据构造函数必须以冒号开头,而普通运算符则永远不能以冒号开头。

构造函数的接口看起来像简单的(可能是部分的)函数应用程序,这使得一个参数的构造函数和大多数新类型非常容易使用(Just "hello")。但是 Haskell 允许您使用类似 OCaml 的记录名称和 {field=value,field=value} 样式,尽管在 Haskell 中您不必强制使用字段名称或此语法。因此,OCaml 仅针对单个字段具有简单的语法,但是 Haskell 允许您拥有多个字段的简单语法。最终,避免字段名称对于大型类型来说是不好的,因为位置函数式语法更难重构。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接