2011-08-19 72 views
9

看到這個例子:爲什麼scala無法在部分方法中推斷出類型?

def hello(a:String, b:String) = println(a + ":" + b) 
val m1 = hello("aaa", _) 
m1("bbb") 

它不能編譯,我需要的類型添加到部分方法:

val m1 = hello("aaa", _: String) 

爲什麼斯卡拉不知道方法的第二個參數helloString

回答

15

Scala的類型推斷是基於流的。方法和函數需要顯式參數類型,用於推斷其他類型。參數類型不能從方法或函數體中推斷出來。然而,有時候,參數類型從外部上下文是已知的,然後不需要標記。兩個例子,

val f: String => Unit = hello("aaa", _) 
val s = Seq(1,2).map(_+1) // Seq[Int].map expects a function of Int argument type 

下面是馬丁奧德斯基關於斯卡拉類型推斷與ML和Haskell相比的侷限性的引用。挑戰包括Scala的超載,記錄選擇和分型,以及需要讓事情變得簡單,

原因Scala沒有辛德雷/米爾納類型推論是, 是很難與這樣的特點結合起來作爲超載( ad-hoc變體,而不是類型類),記錄選擇和子類型。 我不是說不可能 - 存在一些擴展 合併這些功能;事實上,我自己已經對其中的一些 有所顧忌。我只是說在 的練習中很難做到這一點,在這種練習中需要有小型表達式和好的 錯誤消息。這不是一個封閉的情況 - 許多研究人員在這裏努力推動邊界(例如在雷米的 MLF)。但現在它是一個更好的類型推斷和 更好的支持這些功能的權衡。您可以權衡方式 。我們希望與Java集成的事實提示 有利於分類,並遠離Hindley/Milner。

來源:下後Universal Type Inference is a Bad Thing評論。

3

這可能是因爲這個定義存在潛在的歧義,因爲hello可能被重載。

// inside some class context 
def hello(a:String, b:String) = println(a + ":" + b) 
def hello(a:String, b:Int) = println(a + ":" + b.toString) 
val m1 = hello("aaa", _) // which one to choose? 

考慮,它不是隻有你誰可以做val m1 = hello("aaa", _)的。可能有你的班級的用戶,正在做val my_hello = (new C).hello("aaa", _)。然後,通過向原始字符串hello方法添加一個重載來打破源代碼兼容性,因爲突然間不應該清楚應該做什麼。

我不確定這是唯一的原因,但人們當然可以將其視爲安全措施。

+0

但在我的代碼中,只有一個'hello'方法。你的意思是,這個類可能會被擴展,並且子類可能會覆蓋'hello'方法,並破壞代碼? – Freewind

+0

我必須承認,我不確定這是否實際上可以與子類化。但即使沒有它,我認爲它總是要求類型註釋更加一致。否則,如果您選擇添加重載,則所有沒有類型註釋的代碼將突然中斷。 – Debilski

+0

我很好奇,爲什麼在這種情況下,'m1'不僅僅是它的參數類型的多態性。 –

4

說得很簡單,Scala使用參數類型來搜索適當的方法,而不是方法類型來推斷參數的類型。

要做你想做的事情,它必須搜索所有可能的調用hello與兩個參數,其中第一個String - 這可能包括隱式轉換 - 然後,如果找到一個最具體的選項,用它來推斷第二個參數的類型。它將不得不這樣做另外它已經做的所有,減慢甚至更多什麼已經是一個相當慢的編譯。不是不可能的,但它不會那樣做。

相關問題