2014-11-23 79 views
4

考慮以下示例代碼,其中我有一個泛型類型和2個靜態成員構造函數,用於創建上述類型的專用實例。爲什麼F#在這種情況下不能推斷出類型?

type Cell<'T> = { slot: 'T } 
with 
    static member CreateInt x : IntCell = { slot = x } 
    static member CreateString x : StringCell = { slot = x} 
and IntCell = Cell<int> 
and StringCell = Cell<string> 

// Warnings on the next 2 lines 
let x = Cell.CreateInt 123 
let y = Cell.CreateString "testing" 

我想我有必要的類型註釋到位,但F#給了我警告。 E.g:

Warning 2 The instantiation of the generic type 'Cell' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, e.g. 'Cell<_>'.

我怎樣才能使警告消失?

+4

提示:您正在使用_generic_類型的靜態成員。 – ildjarn 2014-11-23 20:21:02

+1

@ildjarn你應該發佈作爲答案:-) – 2014-11-23 20:31:25

回答

4

正如@ildjarn暗示的那樣,Cell是一個泛型類型,編譯器在調用靜態成員時想知道類型'T

// Two ways to fix the compiler warning 
let x = Cell<int>.CreateInt 123 
let y = StringCell.CreateString "testing" 

一種避免指定'T的方法是將創建函數移動到模塊中。

type Cell<'T> = { slot: 'T } 
type IntCell = Cell<int> 
type StringCell = Cell<string> 
module Cell = 
    let createInt x : IntCell = { slot = x } 
    let createString x : StringCell = { slot = x } 

let x = Cell.createInt 123 
let y = Cell.createString "testing" 

然而,因爲你反正指定的函數名所需的類型,下面的語法可以是優選的。

type Cell<'T> = { slot: 'T } 
with 
    static member Create (x : 'T) = { slot = x } 
type IntCell = Cell<int> 
type StringCell = Cell<string> 

let x = IntCell.Create 123 
let y = StringCell.Create "testing" 

// or simply 
let z = Cell<float>.Create 1.0 

感謝@Vandroiy在我Create方法指出缺失的類型約束,他的回答,顯示了編譯器可以推斷'T爲通用型Cell時,它可以通過靜態方法來決定如何調用。

+0

謝謝!請注意,模塊解決方案對我來說不起作用,因爲我的真實示例依賴於可選參數 – rgrinberg 2014-11-24 03:34:41

+0

@rgrinberg和cadull:我不認爲這是好的設計。如果你用'Cell .Create 1.0'代替最後一行,它就和「有效」一樣。 (查看我的回答) – Vandroiy 2014-11-24 12:54:58

+1

爲什麼人們會不斷努力?第二種解決方案,OP顯然與之相關,存在嚴重的問題!問題不僅限於通用類型。 'IntCell.Create「hello」'也編譯沒有問題。 – Vandroiy 2014-11-24 13:33:34

4

編譯器無法確定方法CreateIntCreateFloat的通用參數'T,因爲它與方法的返回類型無關。在的問題,它是有效的寫:

Cell<float>.Create 1.0 // useless type annotation to remove warning 

但是,您可以一樣好寫

Cell<string>.Create 1.0 // Trollolol 

爲了避免這種情況,你需要確保工廠只能生產它的類型拜訪。在泛型類型上聲明工廠時,使用類型註釋將其返回類型的泛型參數等同於所調用類型的泛型參數。

在我看來,複雜的表述增加了混淆。你可以達到預期的效果與

type Cell<'T> = 
    { slot: 'T } 
    static member Create (x : 'T) = { slot = x } 

let x = Cell.Create 123 
let y = Cell.Create "testing" 

注爲x類型標註,與Cell<>類型的泛型參數相當於工廠的輸入型!

編輯以報告的評價:

原樣,類型IntCellStringCell沒有意義;它們只是Cell<int>Cell<string>的可讀性較差的形式。從評論到這個答案,我明白這些類型應該暴露,而不是Cell。據我所知,如果在問題中定義它們,這是不可能的,因爲類型縮寫最多具有它們縮寫類型的可訪問性。

這是一個合理的設計選擇:如果一個類型是泛型的,它應該接受所有有效的泛型類型參數。如果IntCellStringCell添加專門的實現,通常的方法是組合它們的Cell類型及其專用功能的適當實例。然後,Cell類型被允許具有比專門類型更受限制的可訪問性。

+0

單元類型只是一個例子。在我最初的設計中,泛型是沒有意義的,不應該被構造。只有2個專業被暴露 – rgrinberg 2014-11-24 16:34:50

+0

@rgrinberg我不認爲這會工作;專業化是類型縮寫,因此將受到Cell <>類型的可訪問性的限制。見編輯的答案。 – Vandroiy 2014-11-24 17:15:01

+0

很酷的答案,我不知道你可以省略這種類型的通用規範;我習慣看到像'Cell <_>'這樣的代碼。由於它聲明瞭創建函數的返回類型,原始問題在我的回答中沒有表現出問題。我會糾正我的答案。 – cadull 2014-11-25 03:19:46

相關問題