F#代码组织:类型&模块

时间:2010-02-06 18:27:30

标签: f# module code-organization static-members

如何决定在模块内部编写函数还是作为某种类型的静态成员?

例如,在F#的源代码中,有许多类型与同名模块一起定义,如下所示:

type MyType = // ...

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MyType = // ...

为什么不简单地将操作定义为MyType类型的静态成员?

4 个答案:

答案 0 :(得分:25)

以下是关于技术差异的一些注释。

模块可以'打开'(除非他们有RequireQualifiedAccessAttribute)。也就是说,如果您将函数(FG)放入模块(M)中,那么您可以编写

open M
... F x ... G x ...

而使用静态方法,你总是写

... M.F x ... M.G x ...

模块功能无法超载。模块中的函数是let-bound,而let-bound函数不允许重载。如果你想能够同时打电话

X.F(someInt)
X.F(someInt, someString)

您必须使用某种类型的member,这只适用于“合格”的调用(例如type.StaticMember(...)object.InstanceMember(...))。

(还有其他差异吗?我不记得了。)

这些是影响一种选择的主要技术差异。

此外,F#运行时(FSharp.Core.dll)中有一些趋势是仅将模块用于F#特定类型(通常在与其他.Net语言进行互操作时不使用)和API的静态方法更加语言中立。例如,所有带有curried参数的函数都出现在模块中(curried函数对于从其他语言调用来说非常重要)。

答案 1 :(得分:3)

在F#中,我更喜欢类型上的静态成员而不是模块中的函数...

  1. 我必须定义类型而不管成员
  2. 该成员在功能上与我定义的类型
  3. 相关

答案 2 :(得分:2)

除了其他答案之外,还有一个案例可以使用模块:

对于值类型,它们可以帮助定义每次访问时都不会重新评估的静态属性。例如:

type [<Struct>] Point =
    val x:float
    val y:float
    new (x,y) = {x=x;y=y}

    static member specialPoint1 = // sqrt is computed every time the property is accessed
        Point (sqrt 0.5 , sqrt 0.5 )

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Point = 

    let specialPoint2 = // sqrt is computed only once when the Module is opened
        Point (sqrt 0.5 , sqrt 0.5 )

答案 3 :(得分:0)

最初未提及的一些重大区别:

  • 函数是F#中的第一类值,但静态成员不是。所以你可以写objs |> Seq.map Obj.func,但不能写objs |> Seq.map Obj.Member

  • 功能可以是咖喱,但成员不能。

  • 编译器会在调用函数时自动推断类型,但在调用成员时则不会。所以你可以写let func obj = obj |> Obj.otherFunc,但不能写let func obj = obj.Member

由于成员受到更多限制,我通常会使用函数,除非我明确要支持OOP / C#。

相关问题