首页 文章

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

提问于
浏览
17

如何在模块内部编写函数或作为某种类型的静态成员之间做出决定?

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

type MyType = // ...

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

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

4 回答

  • 25

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

    Modules can be 'open'ed (除非他们有RequireQualifiedAccessAttribute) . 也就是说,如果你把函数( FG )放在一个模块( M )中,那么你可以写

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

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

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

    Module functions cannot be overloaded . 模块中的函数是let-bound,而let-bound函数不允许重载 . 如果你想能够同时打电话

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

    您必须使用类型的 member ,该类型仅适用于'qualified'调用(例如 type.StaticMember(...)object.InstanceMember(...) ) .

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

    这些是影响一个选择的主要技术差异 .

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

  • 3

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

    • 我必须定义类型而不管成员

    • 该成员与我定义的类型在功能上相关

  • 0

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

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

    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 )
    
  • 2

    最初没有提到的一些重大区别:

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

    • 功能可以通过咖喱,但会员不能 .

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

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

相关问题