F#基础教程 类型与类型推理

      F#是强类型语言,这意味着你不能使用一个不恰当的值调用一个函数,你不可以用字符串调用一个需要整数参数的函数;必须显式地进行转换。这种涉及到值的类型的语言方式就做类型系统。F#的类型系统在一般语言中并不常见,在F#里,所有的值类型,包括函数(functions)的值,通常情况下并不需要明确声明类型,编译器将根据函数的定义类型和函数被调用的返回类型推理出值的类型。如果全部没有问题,编译器将推理出所有的类型,只有当一个类型不匹配时才会报告类型错误。这个过程一般称为类型推理。如果想在程式里得到更多类型推理的信息,你可以使用-i开关使编译器显示更多信息,Visual Studio用户则可以把鼠标悬停在标识符上,方便的获取类型信息。类型推理在F#里很容易理解,编译器的工作通过程序定义推理标识符的类型,开始于左上角标识符直道最右下角。确定类型的基础是类型的定义和(更常见的)源文件中函数和程序集里的类型定义。下面例子定义了两个F#标识符,然后显示类型推理的类型。这两个标识符的类型很常见,sting和int,编译器描述他们的语法相当简单:关键字val(意思是”value”),然后标识符,一个冒号,最后是类型。

#light
let aString = "Spring time in Paris"
let anInt = 42


val aString : string
val anInt : int

      在下面的例子中定义的函数 makeMessage 更有趣些。你应该注意到两个有关 makeMessage 的情况。首先,它类型使用了前缀关键字val,其后是两个值,这是因为它是一个函数,但F#编译器仍然认为它是一个值。第二,类型本身使用了符号 int->string,这意味着需要一个整数参数并返回一个字符串。-> 两边的类型名称(一个ASCII箭头,或只是箭头)代表函数对类型的转换。值的一提的是,箭头代表值的转换,但不一定类型也转换。因为它可以代表一个值到另一个相同类型的值的转换函数。第二行就是这样一个函数。

#light
let makeMessage x = (string_of_int x) + " days to spring time"
let half x = x / 2


val makeMessage : int -> string
val half : int –> int

      可以部分化的函数与元组的类型不同,可参阅“值与函数”一节的介绍。下例中的函数div1和div2用来说明这一点。可部分化函数div1的类型是 int -> int -> int,表明其参数可以分开传入。比较函数div2具有的类型 int * int -> int,意思是一个函数,需要一对整数,即一个整数元组,并转化为一个整数。而第三个函数divRemainder执行整数除法,并执行求余运算。它的类型是 int -> int -> int * int 。这意味着这是一个curried函数,返回一个整数的数组。

let div1 x y = x / y
let div2 (x, y) = x / y
let divRemainder x y = x / y, x % y


val div1 : int -> int -> int
val div2 : int * int -> int
val divRemainder : int -> int -> int * int

      下面的函数,doNothing,虽然很小,但它的类型很有趣,它具有类型 'a –> 'a ,意思是一个函数,需要一个值类型,并返回相同类型的值。单引号(')开始的变量类型代表任意类型。F#中有一个类型,obj,存在于System.Object中,代表任意类型的值,你可能从其它运行在CLR上的语言中(和许多非CLR语言中)熟悉这个概念。但是,一个变量类型是不一样的,请注意箭头的双方各有一个 'a 。这意味,即使编译器还不知道类型,但它知道返回值的类型与参数的类型相同。类型系统的这个功能,有时也称为类型参数。允许编译器在编译时找到更多类型错误,帮助避免错误。

let doNothing x = x


val doNothing : 'a –> 'a

      一个类型变量,或类型参数的概念,和泛型的概念密切相关。

      下面显示的函数doNothingToAnInt ,是一个值约束的例子;在这种情况下,函数参数x的约束是int。值约束的语法很简单:括号内,标识符名称后跟一个冒号(:)然后是类型名称,有时也称为类型的注释。纯函数通常没有必要写值约束,虽然偶尔也是有益的。最有用的地方是使用.NET库编写F#和使用非托管库进行互操作。在这种情况下,编译器获得的信息很少,因此有必要给它更多的信息消除歧义。它可以限制任何标识符,而不仅仅是函数的参数,虽然更多应用于参数的约定。第三行的列表 stringList 说明如何限制一个标识符。

let doNothingToAnInt (x : int) = x
let intList = [1; 2; 3]
let (stringList : list<string>) = ["one"; "two"; "three"]


val doNothingToAnInt _int : int -> int
val intList : int list
val stringList : string list
     intList 的值是一个整数列表,标识符的类型是int list。这表明该列表包含唯一的整数集。在这种情况下,编译器已经确认其类型为固定项目的int list。任何试图添加任何其他int值到列表都将产生一个编译错误。标识符stringList 有一个类型注解,虽然这是不必要的,因为编译器可以从其项目值推理出类型,它是用来替代类型系统推理的替代语法。你可以在尖括号之间放置类型,而不仅仅是前面放置的类型名称。注意,即使stringList 的类型List<string>(一个字符串列表)约束,编译器仍然会报告此标识符的类型信息。这种语法使F#类型的类型参数看起来更象其他.NET库的泛型类型。

posted @ 2011-11-24 17:01  银河系漫游指南  阅读(477)  评论(0编辑  收藏  举报