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?
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).
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
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 )
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