型態推論可不是Scala所發明,早在以前,一些函數式語言與一些命令式語言都已經出現型態推論。千萬不要以為 Scala 這麼神勇,什麼東西都開創新局,其實它只是參考很多好的觀念,把它加到自己的語言設計中罷了。
支援Type Inference的語言包含(參考wiki http://en.wikipedia.org/wiki/Type_inference)
1. Visual Basic:9.0開始
2. C#:3.0開始
3. Clean
4. Haskell
5. ML
6. OCaml
7. Scala
很多吧!這也突顯出Type Inference的重要性。
可還記得之前的討論,為了要加速開發,以及爭取 Script 的使用者,Scala 想要能夠有 Dynamic Type 的便利性,以及 Static Type 的嚴謹度(嚴謹可以讓 compiler 幫你多做一點事,及早檢查出程式的問題,增加程式的品質),這時型態推論就派上用場。
所謂型態推論是 compiler 可以幫忙推論出型態的地方,你就可以省略指明型態。
「這是什麼意思?」
讓我們回到宣告變數的範例:
var n = 10right hand side 的值10,我們很清楚 10 是一個 Int 的型態,由於「不同型態的兩個變數或值是不可互相指定的」,所以我們知道上述 statement 的 n 應該是 Int 型態(當然也有可能是 Int 的 parent class)。這樣的推論,對 compiler 也不會有太大的困難,很容易幫我們代勞。
如果我們的想法是「現在宣告的變數,與右值(right-hand-side)的型態相同」,我們就可以大膽讓 compiler 幫我們代勞,推論出該變數應該具有的型態。這樣的推論代勞就叫型態推論(Type Inference)。
這種方式是不是很方便,是否有點像使用 Dynamic Type 語言的作法!
其實,type inference 更有用的地方在於 function(或稱method)的 return 型態。
在 Java,每個 method 都需要有 return 的型態,就算沒有 return 型態,你也要指明它是 void。void 可以把它看成是一種特殊的型態,表示不 return 值。
在 Scala,method 一定有 return 型態的堅持仍在,Scala 中每個 method 都需要有 return 型態。但我們在 Scala 中宣告 method,卻通常省略宣告 method 的 return 型態,讓我們的程式看起來更簡潔易懂。我們會希望 compiler 使用型態推論幫忙找出 method 的 return 型態。
method 我們尚未討論,這裡我們先舉個小例,說明型態推論在 method 中的情況。
def f1(n: Int) = { n + 100 }上例我們定義一個 method f1,我們沒有指明 return type,但 compiler 會幫我們找出來。
Scala 使用 method 最後一個執行到的 statement 的值作為整個 method 的 return 值。f1 的 return 值很清楚是 n + 100。因為 n + 100 的型態為 Int,所以 f1 的 return 型態推論出是 Int。
型態推論雖然方便,但注意不要任意延伸!
「嗯,那我瞭解了,型態推論很方便,所以 method 的參數也可以推論吧?」
def f1(n) = {n+100}因為 n + 100,所以 n 唯一的可能型態是 Int,compiler可以幫我自動推論 n 的型態,這樣又可以少打幾個字。
以上就是任意延伸的案例,雖然上述的推論看起來沒有問題(其實還是有問題,n 可能是其他型態,但具有 + 的 method),但在 Scala 中參數是不可以被推論的!method 的一定要明確說明型態,否則 compiler 會有 error。
關於型態推論,結論是
1. 變數可以推論
2. method 的 return type 可以推論
3. method 參數不可推論
參數型態推論是一個容易犯的錯誤,另一個典型的型態推論錯誤延伸是「型態轉換」,我們先看例子
var n = 10 n = 10.5第二個 statement 會 compile 錯誤!
「奇怪,不是會推論嗎? n 在第二個 statement 變成 Float 就好了啊?」
錯誤的原因不是推論的問題,而是搞錯靜態語言的特性。
所謂靜態語言是變數的型態一旦決定後,就不會改變的,這叫靜態語言。
以靜態語言而言,第二個 statement 的意思是要把一個浮點數的值,設定給一個 Int 型態的變數,你說,此時 compiler 會如何做?當然就是告訴你 type mismatch。
請大家不要混淆了 variable initialize 與 variable value assignment。
1. Variable initialize 發生在變數宣告時,Scala 要求變數宣告時需要設定初值,變數宣告時,也需要決定該變數的型態,因此可以使用型態推論。
2.Variable value assignment 是變數宣告之後,重新設定新值,因為變數早在宣告時就已固定型態,這時沒有所謂的型態推論。使用不同的型態設定給變數,隱含型態轉變,compiler 自然不會允許。
「奇怪?可是很多 Script 語言中可以這樣做啊?」
沒錯,比如在 JavaScript 中你確實是可以這樣做。
原因是在於這些 Script 語言使用動態型別,而且有些還支援動態轉換(如把 String 轉成對應的數值資料),所以當你執行第二個 statement 時,script engine會以為你要重新設定 n 的型態,可以馬上幫你轉換。
Scala 是靜態型別的語言,不允許你重新設定變數的型態,當你要將 10.5 指定給 n 時,會以為你型態搞錯了,自然給你一個錯誤。
型態推論雖然好用,但不能有過度的期待,Scala 還是堅守 Static Type 的界線,自動轉換(比如型別的轉換,如 Int 轉成 String,或是 String 轉成 Int)不是可以使用型態推論做到的。
若需要型態的轉換(比如讓用戶輸入一個數值的字串,程式要由字串轉成對應的值),此時仍然需要 explicit 指出,或是使用未來介紹的 implicit conversion 機制才可解決。
沒有留言:
張貼留言