2016-12-28 41 views
3

注:這個問題在一定程度上關係到我previous one,但它實際上是從不同的角度接觸問題上的ToString()對字符串操作的類型推斷

考慮下面的代碼片段:

let toStr a = a.ToString() 
let strOp a = string a 

let intToStr = 5 |> toStr 
let floatToStr = 5.0 |> toStr 

let intStrOp = 5 |> strOp 
let floatStrOp = 5.0 |> strOp //type inference error 

雖然strOp函數使用看起來更優雅的解決方案,並且能夠將單位值轉換爲字符串,它似乎不是真正通用的,因爲它的類型在第一次使用期間受到限制(即使推斷的類型是obj -> string,而不是'a -> string

爲什麼字符串操作符不以這種通用的方式工作?或者我做錯了什麼?

+2

'let inline strOp a = string a' – PetSerAl

+0

這是在這裏成功實現更高主動多態性的着名技巧嗎?如果我的問題是關於這個問題的,爲什麼ToString示例工作?我沒有區別。 –

+5

因爲'toStr'是一個真正的泛型函數,而'strOp'實例化一個帶有靜態解析類型約束的內聯函數,但它本身不是內聯的,所以不會繼承泛型。這可能有助於解釋細節:http://stackoverflow.com/questions/30445828/f-generics-function-overloading-syntax/30446386#30446386 –

回答

4

的區別在於string使用靜態成員的約束(參見the definition)而ToString是任何對象上可用常規方法,因此編譯器將ToString調用作爲通用的代碼,不限制以任何方式實例類型。

靜態約束和(非靜態)泛型的工作方式不同,當他們在(否則無約束)用於let結合的功能:

  • 對於通用代碼,編譯器傳播通用性,並且使let - 你寫的通用函數。

  • 對於靜態成員約束,編譯器根據第一次使用專門化代碼。正如評論中提到的那樣,您可以通過使用inline來避免這種情況,它允許基於靜態成員的泛型以與泛型通用代碼相同的方式進行傳播。

我覺得爲什麼string函數使用靜態解析的類型約束的唯一原因是,它允許它專門爲普通ToString呼籲原始類型,但仍然處理自定義的方式null值對象 - toStr null拋出異常但strOp null返回一個空字符串!

+4

我不認爲處理空值是'字符串'的原因靜態約束。如果你看看[它的定義](https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs#L4485),這些類型專業看起來更加註重性能 - 即爲已知的原始類型生成'call'而不是'callvirt'。而空檢查本身在['anyToString'](https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs#L898)中通過拳擊,沒有靜態限制。 –