问题
How do you decide between writing a function inside a module or as a static member of some type?
For example, in the source code of F#, there are lots of types that are defined along with a equally named module, as follows:
type MyType = // ...
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MyType = // ...
Why don't you simply define the operations as static members of type MyType?
回答1:
Here are some notes about the technical distinctions.
Modules can be 'open'ed (unless they have RequireQualifiedAccessAttribute). That is, if you put functions (F
and G
) in a module (M
), then you can write
open M
... F x ... G x ...
whereas with a static method, you'd always write
... M.F x ... M.G x ...
Module functions cannot be overloaded. Functions in a module are let-bound, and let-bound functions do not permit overloading. If you want to be able to call both
X.F(someInt)
X.F(someInt, someString)
you must use member
s of a type, which only work with 'qualified' calls (e.g. type.StaticMember(...)
or object.InstanceMember(...)
).
(Are there other differences? I can't recall.)
Those are the main technical differences that influence the choice of one over the other.
Additionally, there is some tendency in the F# runtime (FSharp.Core.dll) to use modules only for F#-specific types (that are typically not used when doing interop with other .Net languages) and static methods for APIs that are more language-neutral. For example, all the functions with curried parameters appear in modules (curried functions are non-trivial to call from other languages).
回答2:
In F# I prefer a static member on a type over a function in a module if ...
- I have to define the type irrespective of the member
- The member is functionally related to the type I'm defining
回答3:
In addition to the other answers there is one more case to use Modules:
For value types they can help to define static properties that do not get re-evaluated every time they are accessed. for example:
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:
Some big distinctions that weren't originally mentioned:
Functions are first class values in F#, but static members are not. So you can write
objs |> Seq.map Obj.func
but you can't writeobjs |> Seq.map Obj.Member
.Functions can be curried, but members cannot.
The compiler will infer types automatically when you call a function, but not when you invoke a member. So you can write
let func obj = obj |> Obj.otherFunc
but you can't writelet func obj = obj.Member
.
Since members are more restricted, I usually use functions unless I explicitly want to support OOP/C#.
来源:https://stackoverflow.com/questions/2214162/f-code-organization-types-modules