The answer from @meziantou already gives a nice overview of the options (and how they behave differently), so let me just give a brief summary, or list of recommendations:
-
Use
let
orlet mutable
if you want to define a local value that is visible only within the type (essentially aprivate
field or aprivate
function). Inside a module at top-level, these are publicly accessible and evaluated once.let mutable
at module level creates a single writable field with no backing value. -
You can use
val
to create an auto-property, it is short formember val Foo = .. with get
. From F# this is seen as a field, but it’s internally implemented as a get-property with a backing field to prevent mutation. -
You can use
val mutable
to define a public field, but I wouldn’t recommend this unless you actually need a public field (e.g. some .NET library may require types with this structure). -
Using
member x.Foo = ...
is the best way to expose (read-only) state from a type. Most F# types are immutable, so this is perhaps the most common public member. It is short for a get-only instance property. -
Using
member x.Foo with get() = .. and set(value) ...
is useful when you need to create a get/set property with your own custom code in the gettor and settor. This is sometimes useful when you’re creating a mutable object. -
Using
member val Foo = ... with get, set
is basically the same thing as auto-implemented properties in C#. This is useful if you need a mutable property with a getter and setter that just reads/writes a mutable backing field. -
Using
static let
on a type creates a static (class-level) read-only field, which internally creates a property with a backing field. Usestatic mutable let ...
for a read/write static field without a backing field. -
Using
static val mutable private
creates a static read/write auto-property with a backing field, it cannot be public.