2017-08-14 92 views
1

我正在編寫我自己的Writer monad版本,以進行自我教育。我試圖有一些普遍性(但不是試圖通過靜態解析的類型參數或其他解決方法完全實現/方法)。F#靜態成員,缺少泛型類型的實例 - 可能的錯誤?

我的第一個版本得到一個警告:

type Writer<'w, 'a> = | Writer of 'a * List<'w> with 

    static member sum (l1:'w list) (l2: 'w list) = l1 @ l2 

[<AutoOpen>] 
module WriterMonadMod = 

    module Writer = 
     let apply (mf:Writer<'w, ('a -> 'b)>) (ma:Writer<'w, 'a>) : Writer<'w, 'b> = 
      let (Writer (f, l1)), (Writer (a, l2)) = (mf, ma)  
      let b = f a 
      Writer (b, Writer.sum l1 l2) // Warning, on "Writer.sum", 
//Instantiation of generic type missing, can't be inferred. 

好吧,這是有道理的。我們需要哪一個靜態總和?對於哪些特定的實例類型的Writer?我不知道我是否可以忽略這個警告,或者最終是否可以咬我。所以我嘗試「的.sum」之前把一個類型參數上的「作家」 - 但現在,導致錯誤:

  Writer (b, Writer<'w, 'b>.sum l1 l2) //Error, on "<'w, 'b>", 
//unexpected type arguments. 

這讓我困惑,因爲它看起來像在其他的語法,這樣回答Why can't F# infer the type in this case?Cell<float>.Create 1.0 ;它適用於我,沒有錯誤,沒有警告;並嘗試非泛型類型不會改變我的問題。)

所以我愚弄名稱,區分類型從它的值構造函數,添加「T」現在修復它自己!:

type WriterT<'w, 'a> = | Writer of 'a * List<'w> with 
//... 
    static member sum (l1:'w list) (l2: 'w list) = l1 @ l2 

[<AutoOpen>] 
module WriterMonadMod = 

    module WriterT = 
     let apply (mf:WriterT<'w, ('a -> 'b)>) (ma:WriterT<'w, 'a>) : WriterT<'w, 'b> = 
      let (Writer (f, l1)), (Writer (a, l2)) = (mf, ma)  
      let b = f a 
      Writer (b, WriterT<'w, 'b>.sum l1 l2) //No warning, no error. 
//(With "T" distinguishing the type name from value constructor.) 

Does th是有道理的?爲什麼很明顯,在類型和構造函數中使用相同名稱的通常做法似乎在這裏引起歧義?

(旁註:「T」是不是一個偉大的選擇,我想,因爲這不是一個單子轉換而對於上應用的所有類型註釋的原因是調試。)

更新因托馬斯的回答:

奇怪的是,對我來說,這也可以避免錯誤和警告。 Whildcards發球解決歧義警告!

module WriterT = 
     let apply (mf:WriterT<_,_>) (ma:WriterT<_,_>) : WriterT<_,_> = 
      let (Writer (f, log1)), (Writer (a, log2)) = (mf, ma)  
      let b = f a 
      Writer (b, WriterT<_,_>.sum log1 log2)  

回答

2

在第二種情況下的「意外類型參數」錯誤是非常混亂(甚至可能是一個錯誤)。我認爲這裏發生的事情是,編譯器首先將案例名稱解析爲Writer,然後在找到類型參數時報告錯誤。然後它意識到你實際上是指一種類型並改變計劃。重命名這個類型(在你的最後一個例子中)解決了這個模糊問題。

另一種方式來解決,這是添加RequireQualifiedAccess屬性,它會隱藏類型名稱後面的工會的情況下,所以你必須寫Writer.Writer和第一名稱指的是類型:

[<RequireQualifiedAccess>] 
type Writer<'w, 'a> = | Writer of 'a * List<'w> with  
    static member sum (l1:'w list) (l2: 'w list) = l1 @ l2 

module Writer = 
    let apply (mf:Writer<'w, ('a -> 'b)>) (ma:Writer<'w, 'a>) : Writer<'w, 'b> = 
     let (Writer.Writer (f, l1)), (Writer.Writer (a, l2)) = (mf, ma)  
     let b = f a 
     Writer.Writer (b, Writer<_,_>.sum l1 l2) 

現在你可以輸入Writer<_, _>.sum,它可以工作,因爲類型引用已解析。

+0

謝謝你看Tomas。我想我應該提交一個建議。這對我來說似乎是一個很好的機會,可以親自嘗試查看編譯器,看看我是否可以自己調試,我一直在努力學習。 – RomnieEE

+0

現在,它在我的代碼中引起了我的注意:無法推斷靜態「.sum」的Writer類型的警告消失了_wild card_ type註釋!我發現在'apply'中將'WriterT'上的所有類型參數更改爲'<_,_>'仍然可以避免這種警告。似乎很奇怪,通配符可以修復模糊性。但也許這是因爲我不太瞭解類型推斷是如何工作的。 – RomnieEE