联合类型和交集类型

时间:2011-04-13 18:14:43

标签: programming-languages functional-programming type-systems

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

4 个答案:

答案 0 :(得分:20)

联盟类型

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

  

大多数数据结构都涉及   区别等替代方案   在叶子和内部节点之间   一棵树,或最外层的选择   一段抽象语法的形式。   重要的是,选择决定了   价值结构。例如,   节点有孩子,但叶子有   不,等等。这些概念是   特别是由和类型表示   二进制和,提供了一个选择   有两件事,还有一些假的,   它提供了无选择的选择。

<强>布尔

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

data Bool = True
          | False

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

<强>枚举

枚举是更一般的和类型的例子:具有许多但有限的替代值的那些。

求和类型和空指针

和类型的最佳实际动机示例是通过区分失败案例来区分函数返回的有效结果和错误值。

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

data Maybe a = Nothing
             | Just a

我们可以使用NothingJust标记来区分每个值及其状态,从而区分有效值和无效值。

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

交叉点类型

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

  

最有趣和潜在的   交集类型的有用属性   是他们表达能力的能力   基本上没有限制(虽然   课程有限)信息量   关于程序的组件。

     

有关   例如,加法函数(+)可以   给定类型Int -> Int -> Int ^ Real -> Real -> Real,捕获两者   一般事实是两个真实的总和   数字总是真实的,而且越多   两个总和的专业事实   整数总是一个整数。一个   用于语言的编译器   交叉点类型甚至可能提供   两个不同的对象代码序列   对于(+)的两个版本,一个使用   浮点加法指令和   一个使用整数加法。对于每一个   程序中的+的实例,   编译器可以决定是否两者   参数是整数和生成   更有效的目标代码序列   在这种情况下。

     

这种形式   多态或相干重载   是如此富有表现力,那......一套   程序的所有有效类型   等于完整的表征   该计划的行为

他们让我们对类型中的大量信息进行编码,通过类型理论解释多重继承的含义,给类型类提供类型,

答案 1 :(得分:11)

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

var a;
if (condition) {
  a = "string";
} else {
  a = 123;
}

如果您有联合类型,则可以轻松地将a键入int | string

交集类型的一个用途是描述实现多个接口的对象。例如,C#允许在泛型上使用多个interface constraints

interface IFoo {
  void Foo();
}

interface IBar {
  void Bar();
}

void Method<T>(T arg) where T : IFoo, IBar {
  arg.Foo();
  arg.Bar();
}

此处,arg的类型是IFooIBar的交集。使用它,类型检查器知道 Foo()Bar()都是有效的方法。

答案 2 :(得分: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 ;;

哪个有交集类型

  

(Int,Int) - &gt; Int&amp; (字符串,字符串) - &GT;字符串

但您也可以优化上面的类型并输入上面的函数

(Pos,Pos) -> Pos & 
(Neg,Neg) -> Neg & 
(Int,Int)->Int & 
(String,String)->String.

其中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和类型的对应关系(参见here)。

这里是一个混合联合和交集类型的复杂示例(在“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 ] ;; 

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

MPerson -> Man & FPerson -> Woman

答案 3 :(得分:-1)

例如,对于union类型,可以在不引入实际新类但仅使用类型别名的情况下描述json域模型。

type JObject = Map[String, JValue]
type JArray = List[JValue]
type JValue = String | Number | Bool | Null | JObject | JArray
type Json = JObject | JArray

def stringify(json: JValue): String = json match {
    case String | Number | Bool | Null => json.toString()
    case JObject => "{" + json.map(x y => x + ": " + stringify(y)).mkStr(", ") + "}"
    case JArray => "[" + json.map(stringify).mkStr(", ") + "]"
}