2013-03-15 36 views
4

假設我有以下DU:從F#DU訪問特定的情況下

type Something = 
| A of int 
| B of string * int 

現在我用它的功能是這樣的:

let UseSomething = function 
| A(i) -> DoSomethingWithA i 
| B(s, i) -> DoSomethingWithB s i 

這樣的作品,但我不得不解構DU爲了將它傳遞給DoSomethingWith *函數。我很自然地試圖將DoSomethingWithA定義爲:

let DoSomethingWithA (a: Something.A) = .... 

但編譯器抱怨類型A沒有定義。

這似乎完全符合F#的哲學,希望將參數限制爲Something.A,而不僅僅是任何舊的int,所以我只是以錯誤的方式進行討論?

回答

5

需要注意的重要一點是,AB相同Something的構造。因此,如果您嘗試單獨使用AB個案,您將獲得無窮無盡的模式匹配警告。

國際海事組織,解構所有DU的案件是一個好主意,因爲它是類型安全的,迫使你想辦法處理這些案件,即使你不想。如果您必須以相同的方式重複解構DU,則可能會出現此問題。在這種情況下,下游用戶上定義mapfold功能可能是一個好主意:

let mapSomething fa fb = function 
| A(i) -> fa i 
| B(s, i) -> fb s i 

請參閱優秀Catamorphism series通過@布賴恩瞭解上個DU倍。

這也表示你的例子很好。解構後你真正處理的是stringint

您可以使用Active Patterns分別消耗兩種情況:

let (|ACase|) = function A i -> i | B _ -> failwith "Unexpected pattern B _" 
let (|BCase|) = function B(s, i) -> (s, i) | A _ -> failwith "Unexpected pattern A _" 

let doSomethingWithA (ACase i) = .... 

但推斷類型的doSomethingWithA仍然是相同的,並經過B _的功能,當你得到一個異常。所以做IMO是錯誤的。

+0

感謝鏈接變形 - 看起來像一個偉大的系列,並且你對地圖函數的建議是有道理的。我同意處理一個DU的所有情況是件好事:只要我知道我有一個A就會感覺到,我應該能夠繞過這個A而不是被迫傳遞一個東西。也許我被我的面向對象背景知識絆倒了,A和B都被實現爲Something的子類型? – Akash 2013-03-15 11:21:11

+1

+1「解構所有DU的案例是一個好主意,因爲它是類型安全的,迫使你想到處理這些案例,即使你不想要」 – 2013-03-15 11:41:08

+1

@Akash:你是對的。繼承可以用來模擬DU,但模式匹配和窮舉檢查變得更乏味。就OOP和DU之間的差異(在強度和弱點等方面)而言,我發現這篇論文很有啓發意義,這篇論文http://www.cs.utexas.edu/~wcook/papers/OOPvsADT/CookOOPvsADT90.pdf。 – pad 2013-03-15 12:05:31

3

A不是一種類型,它只是Something的構造函數。你無法避免模式匹配,這並不一定是壞事。

這就是說,F#確實提供了一個名爲活動模式的事情,比如

let (|AA|) = function 
    | A i -> i 
    | B _ -> invalidArg "B" "B's not allowed!" 

然後你就可以使用這樣的:

let DoSomethingWithA (AA i) = i + 1 

但有沒有真正的理由,你爲什麼會想要這樣做!你仍然在底層做相同的舊模式匹配,再加上你冒着運行時錯誤的可能性。

在任何情況下,您實施UseSomething對F#來說都是非常自然的。

+1

查看由Visual Studio對象瀏覽器或IL Spy中的F#編譯器生成的IL代碼總是有益的。在IL中,Something.A和Something.B確實是Something的子類。因此,在非F#語言(例如C#或VB.Net)中,一個_can_將參數聲明爲Something.A或Something.B,這是@Akash建議的方式。 – 2013-03-19 13:27:48

+0

@MarcSigrist這只是部分正確的。 Nullary案例不會生成嵌套類型,而有些分支(如'None'構造函數)用'null'來表示。 – eirik 2013-03-20 15:28:06

+0

是的,當然。在提到的具體情況中確實如此,但在其他情況下並不一定如此。 – 2013-03-21 08:29:28

3

其他答案是準確的:在F#AB是構造函數,而不是類型,這是強類型函數語言如Haskell或ML家族中的其他語言採用的傳統方法。但是,還有其他方法 - 例如,我相信在斯卡拉,AB實際上是Something的子類,因此您可以在適用的情況下使用這些更具體的類型。我並不完全確定在設計決策中涉及哪些折衷,但一般來說,繼承使得類型推斷變得更難/不可能(對於Scala中的刻板印象類型推斷比Haskell或ML語言差得多)。

+0

謝謝,我覺得有趣的是我的心智模型比F#更接近斯卡拉的方法。但是我不會很快就搬到Scala,所以我需要重新思考! – Akash 2013-03-15 15:28:42