问题描述
|
联合类型和交叉类型的用例有哪些?最近,关于这些类型系统功能的讨论很多,但是以某种方式,我从未感到需要这些功能!
解决方法
如果您想要更注重实践的答案:
使用联合和递归类型,您可以对常规树类型进行编码,从而对XML类型进行编码。
使用交集类型,您可以键入两个重载函数和优化类型(在上一篇文章中称为相干重载)
因此,例如,您可以按以下方式编写函数add(重载整数和和字符串连接)
let add ( (Int,Int)->Int ; (String,String)->String )
| (x & Int,y & Int) -> x+y
| (x & String,y & String) -> x@y ;;
具有交叉点类型
(整数,整数)->整数&(字符串,字符串)->字符串
但是您也可以优化上面的类型,并按以下方式键入上面的函数
(Pos,Pos) -> Pos &
(Neg,Neg) -> Neg &
(Int,Int)->Int &
(String,String)->String.
其中Pos和Neg是正整数和负整数。
上面的代码可以用CDuce(http://www.cduce.org)语言执行,其类型系统包括联合,交集和否定类型(主要针对XML转换)。
如果您想尝试它并且在Linux上,那么它可能已包含在您的发行版中(apt-get install cduce或yum install cduce应该可以完成工作),并且您可以使用其顶层(la OCaml)与union一起玩和交叉点类型。在CDuce网站上,您将找到许多使用联合和交叉类型的实用示例。并且由于与OCaml库完全集成(您可以在CDuce中导入OCaml库,并将CDuce模块导出到OCaml),因此您还可以检查与ML sum类型的对应关系(请参见此处)。
在这里,您是一个混合并集和交集类型的复杂示例(在页面“ 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
,联合类型
引用罗伯特·哈珀(Robert Harper)的“编程实践基础”
语言\“,第15章:
大多数数据结构涉及
区分等替代方案
在叶子和内部节点之间
一棵树,或最外面的选择
形式的抽象语法。
重要的是,选择决定了
价值的结构。例如,
节点有孩子,但叶子有
否,依此类推。这些概念是
具体由总和类型表示
二进制和,提供了选择
两件事和零和
没有任何选择。
布尔值
最简单的总和类型是布尔值,
data Bool = True
| False
布尔值只有两个有效值T或F。因此,除了将它们表示为数字之外,我们还可以使用求和类型来更准确地编码只有两个可能值的事实。
枚举
枚举是更一般的总和类型的示例:具有许多但有限的替代值的那些。
总和类型和空指针
求和类型的最佳实践动机示例是通过区分故障情况来区分有效结果和函数返回的错误值。
例如,空指针和文件结尾字符是总和类型的不可靠编码:
data Maybe a = Nothing
| Just a
在这里我们可以使用Nothing
或ѭ7to标记来区分有效值和无效值,并使用状态注释每个值。
通过以这种方式使用求和类型,我们可以完全排除空指针错误,这是一个相当不错的示例。空指针完全是由于较旧的语言无法轻松表示和类型。
交叉点类型
交叉点类型更新得多,其应用还不那么广泛。但是,本杰明·皮尔斯(Benjamin Pierce)的论文(\“交叉点类型编程
和有界多态性\“)给出了很好的概述:
最吸引人且潜在的
交点类型的有用属性
是他们表达能力的能力
本质上是无界的(尽管
课程有限)信息量
关于程序的组成部分。
对于
例如,加法函数(+)
可以是
给定类型“ 9”,同时捕获
一般事实,即两个实数之和
数字永远是真实的,而且
专业事实,即两个
整数始终是整数。一种
语言的编译器
交叉点类型甚至可以提供
两种不同的目标代码序列
对于(+)
的两个版本,一个使用
浮点加法指令和
一种使用整数加法。对于每个
程序中+的实例,
编译器可以决定是否同时
参数是整数并生成
更有效的目标代码序列
在这种情况下。
这种法定的
多态或相干重载
如此富有表现力,...
程序的所有有效键入
完全表征
该程序的行为
他们让我们在类型中编码了很多信息,通过类型理论解释了多重继承的含义,为类型类提供了类型,
,联合类型对于键入动态语言或以其他方式比大多数静态语言所允许的传递的类型更大的灵活性很有用。例如,考虑一下:
var a;
if (condition) {
a = \"string\";
} else {
a = 123;
}
如果您具有联合类型,则很容易将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();
}
这里,arg
的类型是IFoo
和IBar
的交集。使用此类型检查器知道Foo()
和Bar()
都是有效的方法。
,例如,具有联合类型的人可以描述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(\",\") + \"]\"
}