F# 使用类型别名来表示参数语义是标准做法吗?

F# 使用类型别名来表示参数语义是标准做法吗?,f#,tuples,naming,type-alias,F#,Tuples,Naming,Type Alias,元组中的项没有名称,这意味着您通常没有明确的方法来记录每个项的含义 例如,在这个受歧视的联盟中: type NetworkEvent = | Message of string * string * string | ... 我想说明的是,第一项和第二项分别是发件人和收件人的名称。这样做是否是一种良好的做法: type SenderName = string type RecipientName = string type NetworkEvent = | Message of Sender

元组中的项没有名称,这意味着您通常没有明确的方法来记录每个项的含义

例如,在这个受歧视的联盟中:

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#的情况并非如此。

我认为在这种情况下,最好对元组的前两个元素使用记录类型,以强调顺序的重要性


对于数字,您可以使用度量单位做一些稍微优雅一些的事情,但是这对字符串不起作用,例如,我看不出有什么问题,但是对于
字符串
,使用10个别名可能会让人恼火。如果你不介意的话,我说,去吧。就我个人而言,我希望以下内容得到支持:

type NetworkEvent =
| Message of SenderName:string * RecipientName:string * string
| ...
智能感知可以提供一些帮助。(编辑:对该建议进行表决。)


如果您的案例有多个字段,或几个相同的类型,您可以考虑使用类层次结构。

我已经阅读了我在网上和书籍中的F1共享部分,但从未见过有人使用别名作为文档的形式。所以我要说这不是标准的做法。也可以将其视为代码复制的一种形式

通常,特定的元组表示只能用作函数中的临时数据结构。如果要长时间存储一个元组或在不同的类之间传递它,那么是时候做一个记录了

如果要跨多个类使用一个有区别的联合,那么按照您的建议使用记录,或者将所有方法的作用域限定为有区别的联合,如下所示

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")
您可以使用,所以元组实际上是一个方便的问题,应该随着代码的增长被记录所取代

如果一个受歧视的并集有一组具有相同签名的元组,那么是时候将其分解为两个受歧视的并集了。这也将防止您拥有具有相同签名的多个记录

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


我认为,使用类型别名进行文档记录是记录受歧视的联合的一种好的简单方法。我在许多演示中使用了相同的方法(请参阅),我知道有些人也在生产应用程序中使用它。我认为有两种方法可以让定义更加不言自明:

使用类型别名:这样,您可以添加一些在IntelliSense中可见的文档,但它不会通过类型系统传播-当您使用别名类型的值时,编译器会将其视为
字符串
,因此您不会到处看到其他文档

使用单例联合这是一种已在F#编译器的某些地方使用的模式。它使信息比使用类型别名更为可见,因为类型
发送者名称
实际上是与
字符串
不同的类型(另一方面,这可能会有一些小的性能损失):

使用记录:通过这种方式,您可以显式定义一条记录来携带工会案例的信息。这在语法上更加冗长,但它可能以最容易访问的方式添加附加信息。您仍然可以对记录使用模式匹配,也可以使用点符号访问元素。在开发过程中添加新字段也更容易:

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

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

如果存在大量的
网络事件
,那么记录的激增不是同样糟糕吗?当有大量具有类似参数名称的记录时,类型推断会变得非常混乱,最终不得不完全限定每个字段。@ReiMiyasaka——实际上情况并没有那么糟糕。如果你在实例化记录时完全限定了你提到的第一个字段,其他字段将正确解析。是的,F#语法的很多方面使得它不可能拥有像C#一样丰富的智能感知。最近有一项改进允许-“从F#3.1开始,您可以为单个字段指定名称,但名称是可选的,即使同一案例中的其他字段已命名。“根据投票结果,这可能是少数人的观点,但在我看来,冗长的记录或单一案例工会是一个破坏交易的因素。它完全否定了联合的简洁性/句法便利性。这似乎是Intellisense最好解决的问题——只要有一种注释字段的方法——而不是改变类型和编码风格。我认为这个(其他方面很好的)答案可能需要更新。您现在可以执行
type X=Msg of Sender:string*Recip:string
。您也可以使用这些字段:
让x=x(Sender=“Foo”,Recip=“Bar”)
。就像记录一样,但语法不同。在F#3.1中,这些可以有名字。@JamesMoore是的,这让我很高兴:D
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} -> ...