提问人:rwallace 提问时间:5/16/2020 更新时间:7/16/2022 访问量:465
总和类型的结构类型化
Structural typing for sum types
问:
对于产品类型,标称类型与结构类型是一种设计决策,在每种情况下都有直接的解释;您可以定义两个相同的记录类型,这些记录类型具有相同的字段,顺序相同,但名称不同;它们要么兼容,要么不兼容;很容易看出每种可能性如何导致一个连贯的类型系统。
我不清楚这同样适用于总和类型;这样做的重点是保留标记名称,以便您可以创建值并在以后区分它们。但是在名义类型与结构类型的讨论中,我找不到任何关于这个问题的提及。
是不是这样:
- 当然,名义类型与结构类型仅适用于产品类型,总和类型必须是名义类型,这一点是显而易见的,不言而喻,所以没有人费心去提它。
- 实际上,总和类型可以是结构化的,以下方式是我没有想到的......
- 别的?
答:
通过与你聊天,我理解你要问的是,一种新语言是否可以将等效的总和类型视为相同。例如,如果语法类似于 ML,则可以定义
data Val = Unparsed String | Parsed Int
data File = Filename String | FileDescriptor Int
在这里,对于是否被视为与产品类型相同的类型、可转换类型或不相关的类型,您有完全相同的选择。让我们来看看其中的一些选项。Val
File
请注意,运行时必须跟踪 sum 类型的哪个组件处于活动状态,但这不需要是类型名称或类型 ID(除非语言提供了一种直接查询活动类型的方法)。它可以静态地进行所有类型检查,将可能的格式枚举为任意整数,并将运行时与此值进行比较(例如,对大小写匹配进行二进制搜索)。将其用作函数表或其他内容的索引。0x02
结构类型
一种可能的简单实现是鸭子类型化它们。编写一种函数式语言会很奇怪,您可以在其中将 a 传递给任何接受 a 的函数,并且它会正常工作。但它会起作用。该语言将查找定义,查看它们是等价的,并将它们视为彼此的别名。如果语言要求选项具有相同的名称,那么程序员可能会感到惊讶得多。File
Val
标称打字
如果你尝试在 Haskell 中做等效的事情,它会告诉你这是两种不同的类型。要将一个转换为另一个,您需要编写一个解压缩和重新打包的函数,例如
fromVal (Unparsed path) = Filename path
fromVal (Parsed fd) = FileDescriptor fd
糊状的中间
我上面写的转换函数显然是次优的,因为这两种类型具有完全相同的布局和实现。您无需做任何实际工作即可将一个转换为另一个。
该语言可能在这里采取中间立场:您必须在类型之间显式转换,但转换是空操作的。更进一步矛盾的一步可能是要求在某个地方声明来实现这种微不足道的转换,有点像 C++ 或 Haskell 中的构造函数。编译器将能够自动编写它。default
deriving
这在命令式语言中很常见。例如,在 C 语言中,如果两种类型是“布局兼容”的,或者即使它们是前几个字段与布局兼容的产品类型,它们之间的类型双关语也保证有效。
无处不在的套接字库依赖于它来实现实际上的 sum 类型。但是,副作用是,如果您实现了具有 32 位字段和 16 位字段的新网络协议,则该语言将认为该协议与 IPv4 地址和 TCP 或 UDP 端口号兼容。由于类型兼容性是结构性的,因此无法禁用它(甚至无法让语言阻止您搬起石头砸自己的脚,因为类型双关语的方法是覆盖所有类型检查)。但是内核级编程通常需要这种类型双关语。struct sockaddr
评论
答案是案例 #2,sum 类型可以是结构化的,通常称为可扩展和类型或开和类型(或可扩展/开并集类型);world-peace 是实现可扩展 sum 类型的 Haskell 库示例。
OCaml/ReasonML/Rescript 将求和类型称为“变体”,将可扩展求和类型称为“多态变体”。
评论
Val
File