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 )
4 回答
以下是关于技术差异的一些注释 .
Modules can be 'open'ed (除非他们有RequireQualifiedAccessAttribute) . 也就是说,如果你把函数(
F
和G
)放在一个模块(M
)中,那么你可以写而使用静态方法,你总是写
Module functions cannot be overloaded . 模块中的函数是let-bound,而let-bound函数不允许重载 . 如果你想能够同时打电话
您必须使用类型的
member
,该类型仅适用于'qualified'调用(例如type.StaticMember(...)
或object.InstanceMember(...)
) .(还有其他差异吗?我不记得了 . )
这些是影响一个选择的主要技术差异 .
此外,F#运行时(FSharp.Core.dll)中存在一些趋势,即仅将模块用于特定于F#的类型(通常在与其他.Net语言进行互操作时不使用)和用于更多语言的API的静态方法-中性 . 例如,带有curried参数的所有函数都出现在模块中(curried函数对于从其他语言调用来说是非常重要的) .
在F#中,我更喜欢类型上的静态成员而不是模块中的函数...
我必须定义类型而不管成员
该成员与我定义的类型在功能上相关
除了其他答案之外,还有一个案例可以使用模块:
对于值类型,它们可以帮助定义每次访问时都不会重新评估的静态属性 . 例如:
最初没有提到的一些重大区别:
函数是F#中的第一类值,但静态成员不是 . 所以你可以写
objs |> Seq.map Obj.func
但你不能写objs |> Seq.map Obj.Member
.功能可以通过咖喱,但会员不能 .
编译器将在调用函数时自动推断类型,但在调用成员时则不会 . 所以你可以写
let func obj = obj |> Obj.otherFunc
但你不能写let func obj = obj.Member
.由于成员受到更多限制,我通常会使用函数,除非我明确要支持OOP / C# .