F#簡明教程二:F#類型系統和類型推斷機制
原創【51CTO獨家特稿】在上一篇教程《F#與函數式編程概述》中我們了解到F#和函數式編程的一些特點,更多關于F#語言和函數式編程的介紹可以參考51CTO之前對微軟MVP趙頡老師的專訪《TechED 09視頻專訪:F#與函數式編程語言》。本節教程我們將學習到F#的一些基礎原理,在開始之前,讓我們先溫習一下我們的Hello World代碼:
- #light
- System.Console.WriteLine(“This is one hello”)
- printfn “This is another hello”
F#是函數式和面向對象的混合體。它有時候會看起來與C#或Visual Basic驚人的相似,但卻又完全陌生。F#程序以一系列的表達式形式組成,每個表達式可以通過“let”標識符被指定,比如:
- let fles = System.IO.DirectoryInfo(@”C:\Users\Chance”).
- GetFiles()
在上面的代碼中,“fles”被指定了一個值,在這個例子中,是一個文件路徑。有意思的是,程序運行中,直到語句在得到右側的返回值前,“fles”的實際類型都沒有被詳細定義。你可能覺得有些別扭,在Java或其他編程語言中,變量fles應該被定義成一種數據類型,string或是其他什么類型以在內存中可以明確的被編譯器區別對待,但這些規則在F#中有些不同。這也導致我們的F#簡明教程稍有不同,我們不會像通常的教程那樣介紹F#的基本數據類型,從某種意義上說,F#可以是任意類型或只有一個類型。
F#小提示:F#是一種類型推斷語言,它們在編譯過程中被推斷和確定。如果你在Visual Studio中編寫F#,將鼠標指向某個值就會得到它的類型,編譯器可以通過函數體或其他方式的定義推斷出類型;Visual Studio是開發F#的主要工具,51CTO推薦您閱讀Visual Studio 2010中關于F#的資源一文。
類型推斷(Type Inference)
我們說數據的類型是被推斷出的,因為F#的編譯期進程會試圖根據變量自身的特點來判斷出它的類型并確保這種類型是安全的。盡管F#是強類型語言,但變量的類型聲明在類型的判斷推理過程中并不是必須的。
類型推斷有自身的優點。在使用F#開發一些大型應用時,比如.NET和Java開發者都很熟悉的泛型特性(Generics)便是由類型推斷來完成。注意,F#編譯器會視任何沒有類型標注的表達式為泛型。例如,下面的函數中,各變量的類型被定義(推斷為)泛型,即使程序編寫者沒有定義任何類型。
- let f x =
- let y = g x
- h y
- let f (x:’a) : ’b =
- let y:’c = (g:’a->’c) x
- (h:’c->’b) y
F#小提示:在F#中,泛型類型參數是一個以撇號為前綴的字符。比如上面例子中的’b和’c就是最常用的泛型參數。像在.NET中一樣,泛型類型也可使用尖括號語法,比如“Dictionary<’Key,’Value>”。只有一個泛型參數的時候,你有時候會看到它使用‘前綴’語法而不是尖括號——最常見的是和F#泛型類‘list’和‘option’一起使用。比如“int list”和“list<int>”表達同一種功能,只是書寫方式不同。
F#類型推斷機制
F#語言中的大多數類型推斷可以遵循以下兩條規則。首先,如果一個函數用于產生一個值,編譯器將假定該值的類型是函數需求的。第二,如果一個值是一個表達式的必然結果,這個值的類型是這個表達式所決定的。
有些情況下這些簡單的規則不夠完全,編譯器必須需要類型聲明。比如,當一個算數運算符被使用,F#會處理的非常謹慎,如果沒有程序員的明確代碼,不會將一個數值型賦予另一個。這樣做是為了確保F#在進行大規模數值計算時,類型推斷不會加重編譯器的負擔。
針對第二條規則的例子在方法過載的情況下發生。比如Write方法在System.Console(.NET中System.Console封裝了基于控制臺應用程序的輸入、輸出和錯誤流操作)中有18個負載。類型推斷可以確定傳送給它的類型,但是無法確定另一個方向傳送的值的類型。
類型推斷不只是簡單的符號,它還可以用于程序功能的檢測。當你寫了一段代碼,類型推斷功能為這些代碼智能的獲得了指定的類型,這意味著錯誤不會被引入程序。這種機制使F#獲得動態語言的代碼簡潔性的同時保證了完全靜態的類型系統。
更多關于F#的類型和語法基礎請參考:
F#的類型系統和類型推斷機制是學習和理解F#語言的基礎,掌握了這些有利于我們之后的學習。下周我們將繼續F#的學習,一起探究F#的基礎語法。