2011-12-21 66 views
13

元組中的項沒有名稱,這意味着您通常沒有明確的方式來記錄每個項目的含義。使用類型別名來指示參數語義是標準做法嗎?

例如,在此可識別聯合:

type NetworkEvent = 
| Message of string * string * string 
| ... 

我想弄清楚的是,第一和第二項分別是發件人和收件人的名稱。它是很好的做法,做這樣的事情:

type SenderName = string 
type RecipientName = string 

type NetworkEvent = 
| Message of SenderName * RecipientName * string 
| ... 

很多C/C++庫擁有的類型(例如win32.h)激增,但這些語言,即使參數名稱在很多情況下是可選,它仍然可以完成。 F#並非如此。

+3

在F#3.1中,它們可以有名稱。 – 2014-05-02 23:07:56

+0

@JamesMoore是的,這讓我很開心:D – 2014-05-03 03:28:37

回答

10

我覺得用別名類型文檔目的是記錄您的可識別聯合了良好而簡單的方法。我在很多演示中使用了相同的方法(請參閱for example this one),我也知道有些人也將它用於生產應用程序。我認爲有兩種方法可以使定義更加不言自明:

使用類型別名:這樣,你添加一些文檔,可以在智能感知可見,但它不通過類型系統傳播 - 當您使用別名類型的值時,編譯器會將其視爲string,因此您不會在任何地方看到其他文檔。

使用單個案例聯盟這是一種已在F#編譯器的某些地方使用的模式。它使信息比使用類型的別名更爲明顯,因爲一個類型SenderName實際上是不同的類型string(在另一方面,這可能會有一些小的性能損失):

type SenderName = SenderName of string 
type RecipientName = RecipientName of string 
type NetworkElement = 
    | Message of SenderName * RecipietName * string 

match netelem with 
| Message(SenderName sender, RecipientName recipiet, msg) -> ... 

使用記錄:通過這種方式,您可以明確定義一條記錄來攜帶工會案例的信息。這在語法上更加冗長,但它可能以最易於訪問的方式添加附加信息。您仍然可以在記錄上使用模式匹配,也可以使用點符號來訪問元素。在開發過程中添加新字段也更容易:

type MessageData = 
    { SenderName : string; RecipientName : string; Message : string } 
type NetworkEvent = 
    | Message of MessageData 

match netelem with 
| Message{ SenderName = sender; RecipientName = recipiet; Message = msg} -> ... 
+3

基於最高票數這可能是一個少數派的觀點,但冗長的記錄或單個工會是在我的交易斷路器心神。它完全否定工會的簡潔/句法便利。這似乎是一個由Intellisense最好解決的問題 - 只要有註釋字段的方法 - 不要改變你的類型和編碼風格。 – Daniel 2011-12-21 15:36:16

+1

我認爲這個(否則很好)的答案可能需要更新。你現在可以這樣做:輸入X =發送者的消息:string * Recip:string'。你也可以使用這些字段:'let x = X(Sender =「Foo」,Recip =「Bar」)'。就像記錄一樣,但語法不同。 – Abel 2016-09-12 10:39:34

1

我認爲在這種情況下,您最好使用元組的前兩個元素的記錄類型來強化順序很重要的事實。

對於數字,你可以使用計量單位的東西稍微更優雅,但這不是字符串

+0

記錄的擴散不會如此糟糕 - 如果不是更糟,如果有很多'NetworkEvents'?如果有大量具有相似參數名稱的記錄,則類型推斷會變得非常困惑,並且最終不得不完全限定每個字段。 – 2011-12-21 03:01:54

+0

@ReiMiyasaka - 事實上情況並不是那麼糟糕。如果您完全限定了在實例化記錄時提到的第一個字段,則其他字段將正確解析。 – Kit 2014-06-16 11:34:58

1

我看不出什麼毛病這方面的工作,但它可能會招人煩有10別名例如,string。如果這不妨礙你,我說,繼續。我個人希望支持如下內容:

type NetworkEvent = 
| Message of SenderName:string * RecipientName:string * string 
| ... 

然後Intellisense可以提供一些幫助。 (編輯:對此建議投票here。)

如果您的案例有多個字段或幾個相同類型,您可以考慮使用類層次結構。

+0

是的,F#語法有很多方面,使它不可能像C#一樣具有豐富的智能感知。 – 2011-12-21 06:37:22

+0

最近有一個改進允許這樣做 - 「從F#3.1開始,您可以爲單個字段指定一個名稱,但名稱是可選的,即使同一個案例中的其他字段已被命名。」 http://msdn.microsoft.com/en-us/library/dd233226.aspx – 2014-05-02 23:06:53

6

我已經閱讀了我在F#上的票價分享,無論是在互聯網上還是在書本上,但從未見過任何人使用別名作爲文檔形式。所以我要說這不是標準的做法。它也可以被看作是代碼重複的一種形式。

通常,一個特定的元組表示只能用作一個函數中的臨時數據結構。如果您長時間存儲元組或在不同類之間傳遞元組,則是時候創建記錄。

如果您打算在多個類中使用區分的聯合,那麼請按照您的建議使用記錄,或將所有方法的作用域限定爲區分的聯合,如下所示。

type NetworkEvent = 
    | Message of string * string * string 

    static member Create(sender, recipient, message) = 
     Message(sender, recipient, message) 

    member this.Send() = 
     math this with 
     | Message(sender, recipient, message) -> 
      printf "Sent: %A" message 

let message = NetworkEvent.Create("me", "you", "hi") 

您可以使用records in pattern matching,所以元組是真正的方便起見,應由記錄作爲代碼的增長所取代。

如果一個受歧視的工會有一堆具有相同簽名的元組,那麼是時候把它分成兩個有區別的工會。這也會阻止您擁有多個具有相同簽名的記錄。

type NetworkEvent2 = 
    | UDPMessage of string * string * string 
    | Broadcast of string * string * string 
    | Loopback of string * string * string 
    | ConnectionRequest of string 
    | FlushEventQueue 

type MessageType = 
    | UDPMessage 
    | Broadcast 
    | Loopback 

type NetworkEvent = 
    | Message of MessageType * string * string * string 
    | ConnectionRequest of string 
    | FlushEventQueue 
相關問題