如何在 F# 中创建值类型的联合类型?

How to create a Union type in F# that is a value type?

普通 F# 可区分联合是引用类型。如何在 F# 中创建一个 simple(非递归且只有值类型字段)联合类型,它是一个值类型?

基于一些互联网搜索,我当前(无法正常工作)的尝试如下所示:

[<StructLayout(LayoutKind.Explicit)>]
type Float =
    [<DefaultValue>] [<FieldOffset 0>] val mutable Val1 : float
    [<DefaultValue>] [<FieldOffset 0>] val mutable Int1 : int
    new (a:float) = {Val1 = a}    

The following blog post appears to show what is possible via C#

我知道以上不是 F# 的惯用用法,但我正在尝试优化我的应用程序的一部分的性能并且分析清楚地表明堆分配的成本 (JIT_new)是什么导致了我的性能瓶颈......一个简单的联合类型是满足我需求的完美数据结构,而不是堆分配的。

首先,我可能不会这样做,除非我有非常充分的理由。在大多数情况下,结构和引用类型之间的区别并没有那么大——根据我的经验,只有当你有一个非常大的数组时它才重要(然后结构让你分配一个大内存块)。

也就是说,看起来 F# 不喜欢您示例中的构造函数代码。我真的不确定为什么(它似乎正在做一些对重叠结构不太有效的检查),但以下是诀窍:

[<Struct; StructLayout(LayoutKind.Explicit)>]
type MyStruct =
    [<DefaultValue; FieldOffset 0>] 
    val mutable Val1 : float
    [<DefaultValue; FieldOffset 0>] 
    val mutable Int1 : int
    static member Int(a:int) = MyStruct(Int1=a)
    static member Float(f:float) = MyStruct(Val1=f)

如果我真的想使用它,我会添加另一个包含 10 的字段 Tag,具体取决于您的结构代表的情况。然后,您可以使用活动模式对其进行模式匹配,并恢复受歧视联合的一些安全性:

let (|Float|Int|) (s:MyStruct) = 
  if s.Tag = 0 then Float(s.Val1) else Int(s.Int1)

F# 现在支持结构联合,有关详细信息,请参阅 F# RFC FS-1014。简而言之:

// Single case:

[<Struct>]
type UnionExample = U of int * int * bool

// Multi-case:

[<Struct>]
type Shape =
   | Circle of radius: double
   | Square of side: int

Key differences in struct records:

  • You cannot have cyclic references to the same type being defined. ex: type T = U of T
  • You also cannot call the default ctor, like you could with normal F# structs.
  • For multi-case struct unions, each case must have a unique name.