Types F#:组织类型和模块的最佳实践

Types F#:组织类型和模块的最佳实践,types,module,f#,naming,Types,Module,F#,Naming,如果我采用函数方法定义一个类型,然后描述在该类型上操作的函数(与实例方法相反),那么我应该如何组织代码 我通常会选择以下三种方式之一: (1) : (2) : (1)的优点是函数是在模块中定义的。(1)的缺点是类型也是在模块中定义的,这导致实例化时出现丑陋的MyType.MyType冗余,并且如果您想允许打开MyType作为冗余的解决方法,则无法[] (2)的优点是在模块中定义的功能,没有模块类型冗余。缺点是模块不能与类型同名 (3)的优点是这些方法是静态的,没有模块类型冗余。缺点是不能在此方法

如果我采用函数方法定义一个类型,然后描述在该类型上操作的函数(与实例方法相反),那么我应该如何组织代码

我通常会选择以下三种方式之一:

(1) :

(2) :

(1)的优点是函数是在模块中定义的。(1)的缺点是类型也是在模块中定义的,这导致实例化时出现丑陋的
MyType.MyType
冗余,并且如果您想允许
打开MyType
作为冗余的解决方法,则无法
[]

(2)的优点是在模块中定义的功能,没有模块类型冗余。缺点是模块不能与类型同名

(3)的优点是这些方法是静态的,没有模块类型冗余。缺点是不能在此方法下定义某些类型(例如非方法值和活动模式)

通常情况下,我更喜欢(2),尽管我讨厌给模块命名,因为它的描述性和直观性不如它应该的那样。(2) 还允许我使用
类型创建相互依赖的类型。。。而且…
在我需要这样做的罕见情况下,对于在单独模块中定义的类型(如方法(1))来说,这显然是不可能的


我的F#程序员同伴们采取了什么方法?我想知道我是否忽略了一些明显的(或不太明显的)东西,或者,如果我没有,是否有一种约定可以解决无法在同一名称空间中以与其对应类型相同的名称命名模块的问题。

还有第四种方法没有列出,那就是将类型和模块命名相同。你认为这是不可能的,但实际上这是一种非常普遍的做法;您只需(在4.1之前的F#版本中)在模块上使用
[]
属性即可。到处写有点难看,这就是为什么定义一个与模块同名的类型。要了解它是如何完成的,请查看FSharpx.Collections代码(以及许多其他项目)。这里有一个文件不是太大,这是一个很好的例子:

从C#开始:

Queue q=新队列({1,2,3},{});//不是有效的列表语法,但不管怎样
Queue moreItems=QueueModule.Conj(4,q);
Queue stillMoreItems=moreItems.Conj(5);

Queue.conj
函数从F#开始使用看起来更自然,而成员方法从C#开始使用
moreItems.conj
看起来更自然,但这两种语言都可用。

也遵循“与类型同名的模块”模式(尽管没有附加到类型的等效方法).有趣的选择,使用CompilationRepresentation属性(我以前没见过,谢谢你的提示)。我给你这个答案的复选标记,因为这是对我问题的一个很好的回答,尽管就C#互操作性而言(这对我来说是个问题),它基本上与我列出的三种方法中的第二种相同。另外,我不喜欢在成员方法中编写功能代码;我更喜欢在相应的模块中这样做,因为我喜欢将我的类型仅限于属性的声明(并且没有任何附加的函数)。只是出于好奇,如果队列是一种记录类型,您希望将其变成C#的惯用形式(因为我倾向于将我的函数与类型分离,所以我经常使用记录),那么如何定义要在成员方法中使用的活动模式呢?如果您不喜欢在成员中实现功能,可以在模块函数中提供实现,然后使用来增强您的类型和所需的成员。
module MyType =
    type MyType (x : int) =
        member val X = x with get

    let myFunction myType y = myType.X + y
type MyType (x : int) =
    member val X = x with get

[<RequireQualifiedAccess>]
module MyTypeOps =
    let myFunction myType y = myType.X + y
type MyType =
    {
        x : int
    }

    static member MyFunction myType y = myType.x + y
namespace FSharpx.Collections

type Queue<'T> (front : list<'T>, rBack : list<'T>) = 
    // ...
    member this.Conj x = 
        match front, x::rBack with
        | [], r -> Queue((List.rev r), [])
        | f, r -> Queue(f, r)
    // ...

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Queue =
    // ...
    let inline conj (x : 'T) (q : Queue<'T>) = (q.Conj x) 
    // ...
let q = new Queue([1;2;3], [])
let moreItems = q |> Queue.conj 4
let stillMoreItems = moreItems.Conj 5
Queue<int> q = new Queue<int>({1,2,3}, {});  // Not valid list syntax, but whatever
Queue<int> moreItems = QueueModule.Conj(4, q);
Queue<int> stillMoreItems = moreItems.Conj(5);