联合类型和交集类型

mis*_*tor 19 type-systems programming-languages functional-programming

联合类型和交集类型的各种用例有哪些?最近有很多关于这些类型系统功能的嗡嗡声,但不知怎的,我从来没有觉得需要其中任何一个!

Don*_*art 21

联盟类型

引用Robert Harper,"编程语言的实用基础",第15章:

大多数数据结构涉及替代方案,例如树中的叶子和内部节点之间的区别,或者抽象语法的最外层形式中的选择.重要的是,选择决定了值的结构.例如,节点有子节点,但叶子没有,等等.这些概念用和类型表示,特别是二元和,它提供了两种选择,而nullary和,它提供了无选择的选择.

布尔

最简单的和类型是布尔值,

data Bool = True
          | False
Run Code Online (Sandbox Code Playgroud)

布尔值只有两个有效值,T或F.因此,我们可以使用求和类型来更准确地编码只有两个可能值的事实,而不是将它们表示为数字.

枚举

枚举是更一般的和类型的示例:具有许多但有限的替代值的类型.

求和类型和空指针

和类型的最佳实际激励示例是通过区分故障情况来区分有效结果和函数返回的错误值.

例如,空指针和文件结束字符是和类型的hackish编码:

data Maybe a = Nothing
             | Just a
Run Code Online (Sandbox Code Playgroud)

我们可以通过使用Nothingor Just标签来注释每个值及其状态来区分有效值和无效值.

通过以这种方式使用sum类型,我们可以完全排除空指针错误,这是一个相当不错的激励示例.空指针完全是由于旧语言无法轻易表达和类型.

交叉类型

交叉点类型更新,它们的应用程序并没有被广泛理解.然而,本杰明·皮尔斯的论文("交叉类型与有界多态性编程")给出了一个很好的概述:

交集类型最有趣和潜在有用的属性是它们能够表达关于程序组件的基本无界限(尽管当然有限)的大量信息.

例如,(+)可以给出加法函数的类型Int -> Int -> Int ^ Real -> Real -> Real,捕获两个实数之和总是实数的一般事实,以及两个整数之和总是整数的更专业的事实.具有交集类型的语言的编译器甚至可以为两个版本提供两个不同的对象代码序列(+),一个使用浮点加法指令,另一个使用整数加法.对于程序中的每个+实例,编译器可以决定两个参数是否都是整数,并在这种情况下生成更有效的目标代码序列.

这种多元多态或相干重载是如此富有表现力,以至于...程序的所有有效类型的集合等于对程序行为的完整表征

他们让我们在类型中编码很多信息,通过类型理论解释多重继承的含义,给类型类型,

  • Sum 类型不是联合类型。有关并集和交集类型的完整处理,请参阅 [http://www.cs.cmu.edu/~joshuad/papers/intcomp/]。 (2认同)

mun*_*ent 11

联合类型对于键入动态语言非常有用,或者在传递的类型中允许比大多数静态语言允许的更多灵活性.例如,考虑一下:

var a;
if (condition) {
  a = "string";
} else {
  a = 123;
}
Run Code Online (Sandbox Code Playgroud)

如果你有工会的类型,可以很容易地键入a作为int | string.

交集类型的一个用途是描述实现多个接口的对象.例如,C#允许对泛型的多个接口约束:

interface IFoo {
  void Foo();
}

interface IBar {
  void Bar();
}

void Method<T>(T arg) where T : IFoo, IBar {
  arg.Foo();
  arg.Bar();
}
Run Code Online (Sandbox Code Playgroud)

在这里,arg"S型的交集IFooIBar.使用它,类型检查器知道它们 Foo()并且Bar()是有效的方法.


小智 9

如果你想要一个更注重实践的答案:

使用union和递归类型,您可以编码常规树类型,从而编码XML类型.

使用交集类型,您可以键入BOTH重载函数和细化类型(之前的帖子称为相干重载)

因此,例如,您可以编写函数add(重载整数和字符串连接),如下所示

let add ( (Int,Int)->Int ; (String,String)->String )
      | (x & Int, y & Int) -> x+y
      | (x & String, y & String) -> x@y ;;
Run Code Online (Sandbox Code Playgroud)

哪个有交叉类型

(Int,Int) - > Int&(String,String) - > String

但是你也可以改进上面的类型并输入上面的函数

(Pos,Pos) -> Pos & 
(Neg,Neg) -> Neg & 
(Int,Int)->Int & 
(String,String)->String.
Run Code Online (Sandbox Code Playgroud)

其中Pos和Neg是正整数类型和负整数类型.

上面的代码可以用CDuce(http://www.cduce.org)语言执行,其类型系统包括union,intersection和negation类型(它主要针对XML转换).

如果你想尝试它并且你在Linux上,那么它可能包含在你的发行版中(apt-get install cduce或yum install cduce应该做的工作)你可以使用它的toplevel(a la OCaml)来玩和交叉类型.在CDuce站点上,您将找到许多使用union和intersection类型的实际示例.由于与OCaml库完全集成(您可以在CDuce中导入OCaml库并将CDuce模块导出到OCaml),您还可以检查与ML和类型的对应关系(请参见此处).

这里是一个混合联合和交集类型的复杂示例(在"http://www.cduce.org/tutorial_overloading.html#val"页面中进行了解释),但为了理解它,您需要了解正则表达式模式匹配,需要一些努力.

type Person   = FPerson | MPerson 
type FPerson  = <person gender = "F">[ Name Children ] 
type MPerson  = <person gender = "M">[ Name Children ] 
type Children = <children>[ Person* ] 
type Name     = <name>[ PCDATA ]

type Man = <man name=String>[ Sons Daughters ]
type Woman = <woman name=String>[ Sons Daughters ]
type Sons = <sons>[ Man* ]
type Daughters = <daughters>[ Woman* ]

let fun split (MPerson -> Man ; FPerson -> Woman)
  <person gender=g>[ <name>n <children>[(mc::MPerson | fc::FPerson)*] ] ->
  (* the above pattern collects all the MPerson in mc, and all the FPerson in fc *)
     let tag = match g with "F" -> `woman | "M" -> `man in
     let s = map mc with x -> split x in
     let d = map fc with x -> split x in    
     <(tag) name=n>[ <sons>s  <daughters>d ] ;; 
Run Code Online (Sandbox Code Playgroud)

简而言之,它将Person类型的值转换为类型的值(Man | Women)(其中竖线表示联合类型)但保持类型之间的对应关系:split是具有交集类型的函数

MPerson -> Man & FPerson -> Woman
Run Code Online (Sandbox Code Playgroud)