求和类型的结构类型

Structural typing for sum types

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

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

是这样吗:

  1. 当然nominal vs structural typing只适用于product types,sum types必须是nominal,这是显而易见的,不用说,所以没有人提到它。
  2. 其实和类型可以是结构化的,下面是我没想到的方式...
  3. 还有别的吗?

通过与您的交谈,我了解到您在问的是一种新语言是否可以将等效的总和类型视为相同。例如,如果语法类似于 ML,您可以定义

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

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

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

结构类型

一种可能的、简单的实现方式是对它们进行 duck-type。编写一种函数式语言会很奇怪,您可以将 File 传递给任何接受 Val 的函数并且它会正常工作。但它会起作用。该语言会查找定义,发现它们是等价的,并认为它们是彼此的别名。如果语言要求选项具有相同的名称,它可能不会让程序员感到惊讶。

标称打字

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

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

糊状中间

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

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

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

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