为什么Haskell没有单个元素元组?

Tak*_*iya 27 haskell

我想知道为什么Haskell没有单个元素元组.是因为到目前为止还没有人需要它,或者是出于任何合理的原因?我在Real World Haskell的网站http://book.realworldhaskell.org/read/types-and-functions.html#funcstypes.composite的评论中找到了一个有趣的帖子,人们猜到了各种原因,如:

  • 没有好的语法糖.
  • 这没用.
  • 您可以认为像(1)这样的正常值实际上是单个元素元组.

但有人知道除猜测之外的原因吗?

scl*_*clv 34

有一个lib!

http://hackage.haskell.org/packages/archive/OneTuple/0.2.1/doc/html/Data-Tuple-OneTuple.html

实际上,我们有一个我们一直使用的OneTuple.它被称为Identity,现在被用作新mtl中标准纯monads的基础:

http://hackage.haskell.org/packages/archive/transformers/0.2.2.0/doc/html/Data-Functor-Identity.html

它有一个重要的用途!通过提供类型的类型构造函数* -> *,它可以成为Monad,Functor等的实例(允许但不是微不足道的实例),它允许我们将它用作变换器堆栈的基础.


Raf*_*ler 9

确切的原因是因为它完全没必要.如果你能拥有它的价值,你为什么还需要一个元组?

语法也往往有点笨拙.在Python中,您可以拥有一个元组,但是需要一个尾随逗号来区分它与带括号的表达式:

onetuple = (3,)
Run Code Online (Sandbox Code Playgroud)

总而言之,没有理由.我确信没有"官方"原因,因为Haskell的设计者可能从未考虑过单个元素元组,因为它没有用处.

我不知道你是否在寻找除了显而易见的原因,但在这种情况下,明显的答案是正确的.

  • @luqui在Python中,元组基本上是一个不可变的列表. (6认同)
  • @luqui:Python中的元组*不是*可变的. (5认同)
  • 请注意,一个元组在底部方面会有不同的行为:正如`(⊥,⊥)::(a,b)`与`⊥::(a,b)`不同,所以将'OneTuple⊥:: OneTuple a`与`⊥:: OneTuple a`区别开来.我不能想象它们有用,请注意,但值得注意.(并且Python的语法会与有用的`-XTupleSections`冲突.) (4认同)
  • @luqui:为什么存在?当然存在元组因为它具有不可变的复合类型是有用的.我怀疑1元组(以及0元组`()`存在的一般性和一致性.使用Python的动态类型,类型的通用性非常有用,因为您并不总是提前知道某些类型的精确类型(尽管您可能知道它是某个未指定大小的"元组").使用Haskell的静态类型,你*总是*知道什么类型的东西,所以任何时候你认为你想要1元组你可以简单地使用标量. (3认同)
  • 我相信python中的元组是可变的(我可能是错的).这使得python元组表现为"引用单元格",这不适用于Haskell,因为一切都是不可变的. (2认同)

Ant*_*lly 5

我的答案并不完全是关于 Haskell 语义,而是关于使一个值与其一元组相同的理论上的数学优雅。(所以这个答案不应该被视为对 Haskell 实现预期的标准行为的解释,因为它并不是这样的。)

\n

在所有函数都被柯里化的编程语言和计算模型中,例如lambda 演算组合逻辑,每个函数都只有一个输入参数和一个输出/返回值。不多也不少。

\n

当我们希望某个特定函数f有多个输入参数 \xe2\x80\x93(例如 3 \xe2\x80\x93)时,我们通过创建一个返回 2 参数函数的 1 参数函数在这种柯里化机制下模拟它。因此,fxyz = ((fx) y) z,并且fx将返回一个 2 参数函数。

\n

同样,有时我们可能希望从函数返回多个值。在这种语义下,这实际上是不可能的,但我们可以通过返回一个元组来模拟它。我们可以概括这一点。

\n

如果为了一致性,我们将任何函数的唯一返回值限制为 (n-) 元组,那么我们就能够将单位和所谓的非元组返回值的一些有趣特征与一般元组的特征相协调, 如下。

\n

让我们采用以下模式作为 n 元组的一般语法,其中c i是索引为i的分量:

\n

(c_1, c_2, \\点, c_{n-1}, c_n)

\n

请注意,此语法中 n 元组具有分隔括号。

\n

在这种模式下,我们如何表示 0 元组?由于它没有组件,因此这种退化情况将表示如下:( )。这种语法与我们用来表示单位值的语法完全一致。因此,我们倾向于使单位值与 0 元组相同。

\n

那么 1 元组呢?它将有这样的表示:(c_1)。这里会立即出现语法歧义:括号在语言中既用作一元组分隔符,又用作值或表达式的单纯分组。因此,在出现(v)的上下文中,编译器或解释器将不确定这是一个具有值为v的组件的 1 元组,还是只是多余括号内的一个孤立值v 。

\n

解决这种歧义的一种方法是强制一个值与将其作为唯一组成部分的 1 元组相同。不会牺牲太多,因为我们可以对 1 元组执行的唯一非空投影是获取其唯一值。

\n

为了一致地执行这一点,语法结果是我们必须稍微放松我们之前的要求,即分隔括号对于所有 n 元组都是强制性的:现在它们对于 1 元组来说是可选的,并且对于所有其他值是强制性的名词​ (或者我们可以要求所有值都用括号分隔,但这在实际使用中会很不方便。)

\n

总之,在 1 元组与其唯一组件值相同的解释下,我们可以通过使用括号进行句法双关,将我们的编程语言或计算模型中函数的所有返回值视为 n 元组: 0 -元组(对于单位类型),1-元组(对于我们通常不认为是元组的普通/“原子”值),以及对/三元组/四元组/...对于其他类型的元组。这种非正统的解释在数学上是简洁和统一的,具有足够的表现力来模拟具有多个输入参数和多个返回值的函数,并且与 Haskell 并不不兼容(从某种意义上说,如果程序员假设这种非官方解释,也不会造成任何伤害)。

\n

这是一个句法双关的论证。无论您满意与否,我们都可以做得更好。通过探索笛卡尔积运算,可以从数学关系理论中得到更有原则的论证。

\n

( n-adic ) 关系被扩展地定义为 (n-) 元组的统一集合。(这种特征是关系数据库理论的基础,因此对于专业计算机程序员来说是重要的知识。)

\n

二元关系 \xe2\x80\x93 一组对 (2 元组) \xe2\x80\x93 是 2 个集合的笛卡尔积的子集:\nR_2 \\子集S \\乘T。对于齐次关系R_2 \\子集S \\乘S = S^2

\n

三元关系 \xe2\x80\x93 一组三元组 (3 元组) \xe2\x80\x93 是 3 个集合的笛卡尔积的子集:\nR_3 \\subseteq S \\times T \\times U。对于齐次关系:R_3 \\subseteq S \\times S \\times S = S^3

\n

一元关系 \xe2\x80\x93 一组单元(1 元组)\xe2\x80\x93 是 1 个集合的笛卡尔积的子集:\nR_1 \\子集 S = S^1(按照通常的数学惯例)。

\n

正如我们所看到的,一元关系只是一组原子值!这意味着一组 1 元组是一组原子值。因此,可以方便地认为 1 元组和原子值是同一件事。

\n

  • 我会犹豫是否要在这里提出任何“数学优雅”的论点。如果元组表示笛卡尔积,那么 `((a,b),c)` 和 `(a,(b,c))` 应该是相同的类型,但它们不是。Haskell 的整个数学支持通常只支持“同构”和“如果我们忽略 ⊥”。但是这样你就不能对 1 元组是否应该是一个东西做出任何争论,因为它们无论如何都与所包含的类型同构。 (2认同)