2017-04-20 84 views
2

爲什麼這個說法給我一個類型不匹配錯誤,瞭解類型不匹配錯誤?

let x = List.rev [] in (3::x, true::x);; 

而這種說法不?

let x = [] in (3::x, true::x);; 

我猜想這是因爲x被給出的第一條語句的函數調用,而它僅在第二條語句給一個空列表。但是,我不確定爲什麼第二個工作,第一個不工作?任何幫助都感激不盡。謝謝!

回答

3

嘗試以下操作:

let x = [] ;; 

結果:val x : 'a list。 F#知道x是一個尚未知類型的列表。如果它有任何內容,它的類型將是已知的。這工作得很好。

不過,下列措施工作:

let x = List.rev [] ;; 

結果:

錯誤FS0030:值限制。值「X」已經被推斷爲有通用型

val x : '_a list  

要麼定義「X」作爲一個簡單的數據來看,使它成爲一個功能有明確的參數,或者,如果你不打算爲它是通用的,添加一個類型註釋。

在F#的「值約束」的錯誤可能是很難理解 - 爲什麼[]時,允許List.rev []是不是? - 但this article進入一些細節。從本質上講,F#總能感到安全作出功能通用的,但它只能感到安全作出通用如果以下兩個條件:

  1. let的右手邊的表達式是值,例如它沒有副作用,
  2. let的右手側的表達式是不可改變

當表達式是[],那麼F#知道,這是一個純粹的,不可變的值,所以它可以使它通用('a list)。但是當表達式爲someFunction []時,F#編譯器不知道要做什麼someFunction。儘管在這種情況下,我們知道List.rev是標準庫的一部分,並且匹配那兩種情況,F#編譯器不能知道。像Haskell這樣的完全純語言,你可以從函數的類型簽名中知道它是純粹的,但F#是一種更實用的語言。所以F#採取保證安全的方法,不是使List.rev []通用的結果。

因此,當你寫let x = [] in (3::x, true::x),該[]值是通用空列表,所以它可以成爲一個既或int list根據需要bool list。但是當你編寫let x = List.rev [] in (3::x, true::x)時,F#不能使List.rev []通用。它可以說「這是一個我還不知道的類型的列表」,並等待類型變得清晰。然後,使用此列表的特定類型的第一個表達式(在本例中爲3::x)將「鎖定」該列表的類型。即,F#不能認爲這個列表是通用的,所以現在已經發現這個空列表是一個空列表int s。然後,當您嘗試將bool附加到空的int list時,那是一個錯誤。

如果將元組翻轉過來以使其爲true::x, 3::x,那麼類型錯誤也會「翻轉」:它會預計bool list並找到int list

所以這個答案的簡短版本是:你正在達到F#值限制,儘管這並不是很明顯,因爲你得到的錯誤根本沒有提到數值限制。

請參閱Understanding F# Value Restriction Errors以獲得對價值限制的良好討論,包括您通常會看到的最常見的地方(部分應用函數)。