总和类型的结构类型化

Structural typing for sum types

提问人:rwallace 提问时间:5/16/2020 更新时间:7/16/2022 访问量:465

问:

对于产品类型,标称类型与结构类型是一种设计决策,在每种情况下都有直接的解释;您可以定义两个相同的记录类型,这些记录类型具有相同的字段,顺序相同,但名称不同;它们要么兼容,要么不兼容;很容易看出每种可能性如何导致一个连贯的类型系统。

我不清楚这同样适用于总和类型;这样做的重点是保留标记名称,以便您可以创建值并在以后区分它们。但是在名义类型与结构类型的讨论中,我找不到任何关于这个问题的提及。

是不是这样:

  1. 当然,名义类型与结构类型仅适用于产品类型,总和类型必须是名义类型,这一点是显而易见的,不言而喻,所以没有人费心去提它。
  2. 实际上,总和类型可以是结构化的,以下方式是我没有想到的......
  3. 别的?
类型 函数式编程 语言不可知论 判别联合

评论

1赞 5/16/2020
Typescript 追求结构类型。当您忽略名称时,标记就会成为该结构的一部分。我认为这种方法相当不合理,但这就是他们的做法。
0赞 rwallace 5/16/2020
@bob 嗯!我不知道打字稿,但如果我正确理解了该代码片段,它似乎正在将名称变成字符串字段?从而实现名义类型——名称作为一个区别因素仍然存在——而没有这样的名义类型系统?
1赞 5/17/2020
完全。他们称之为可区分的联合(标记联合的同义词),其中特殊属性扮演判别器的角色,其值表示标记。如您所见,判别器的每个值都对应于类型构造函数的名称。这是相当令人困惑的部分,因为通常值构造函数的名称提供标记,这些标记在编译过程中不会被擦除。
1赞 5/17/2020
因此,根据 Davislor 的回答,这两个数据声明 / 在 TS 中将被视为相等。这感觉是错误的,因为结构类型忽略了数据类型的语义部分,而这些语义未反映在其结构中。ValFile

答:

2赞 Davislor 5/16/2020 #1

通过与你聊天,我理解你要问的是,一种新语言是否可以将等效的总和类型视为相同。例如,如果语法类似于 ML,则可以定义

data Val  = Unparsed String | Parsed Int
data File = Filename String | FileDescriptor Int

在这里,对于是否被视为与产品类型相同的类型、可转换类型或不相关的类型,您有完全相同的选择。让我们来看看其中的一些选项。ValFile

请注意,运行时必须跟踪 sum 类型的哪个组件处于活动状态,但这不需要是类型名称或类型 ID(除非语言提供了一种直接查询活动类型的方法)。它可以静态地进行所有类型检查,将可能的格式枚举为任意整数,并将运行时与此值进行比较(例如,对大小写匹配进行二进制搜索)。将其用作函数表或其他内容的索引。0x02

结构类型

一种可能的简单实现是鸭子类型化它们。编写一种函数式语言会很奇怪,您可以在其中将 a 传递给任何接受 a 的函数,并且它会正常工作。但它会起作用。该语言将查找定义,查看它们是等价的,并将它们视为彼此的别名。如果语言要求选项具有相同的名称,那么程序员可能会感到惊讶得多。FileVal

标称打字

如果你尝试在 Haskell 中做等效的事情,它会告诉你这是两种不同的类型。要将一个转换为另一个,您需要编写一个解压缩和重新打包的函数,例如

fromVal (Unparsed path) = Filename path
fromVal (Parsed fd)     = FileDescriptor fd

糊状的中间

我上面写的转换函数显然是次优的,因为这两种类型具有完全相同的布局和实现。您无需做任何实际工作即可将一个转换为另一个。

该语言可能在这里采取中间立场:您必须在类型之间显式转换,但转换是空操作的。更进一步矛盾的一步可能是要求在某个地方声明来实现这种微不足道的转换,有点像 C++ 或 Haskell 中的构造函数。编译器将能够自动编写它。defaultderiving

这在命令式语言中很常见。例如,在 C 语言中,如果两种类型是“布局兼容”的,或者即使它们是前几个字段与布局兼容的产品类型,它们之间的类型双关语也保证有效。

无处不在的套接字库依赖于它来实现实际上的 sum 类型。但是,副作用是,如果您实现了具有 32 位字段和 16 位字段的新网络协议,则该语言将认为该协议与 IPv4 地址和 TCP 或 UDP 端口号兼容。由于类型兼容性是结构性的,因此无法禁用它(甚至无法让语言阻止您搬起石头砸自己的脚,因为类型双关语的方法是覆盖所有类型检查)。但是内核级编程通常需要这种类型双关语。struct sockaddr

评论

2赞 Han Seoul-Oh 7/16/2022
仅供参考,有很多 Haskell 库提供了结构化(可扩展的)求和类型,例如 world-peace(其文档也解释了为什么需要它们;错误处理是一个典型的例子),而 OCaml 实际上内置了它们(“多态变体”)作为名义求和类型(“变体”)的替代品
1赞 Han Seoul-Oh 7/16/2022 #2

答案是案例 #2,sum 类型可以是结构化的,通常称为可扩展和类型或开和类型(或可扩展/开并集类型);world-peace 是实现可扩展 sum 类型的 Haskell 库示例。

OCaml/ReasonML/Rescript 将求和类型称为“变体”,将可扩展求和类型称为“多态变体”。