When should I use let, member val and member this.?

F#

F# Problem Overview


F# has many different ways to define variables/members in types. When should I use let, member val and member this. in F#, and what is the difference between them? How about static and mutable members?

F# Solutions


Solution 1 - F#

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 or let mutable if you want to define a local value that is visible only within the type (essentially a private field or a private 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 for member 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. Use static 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.

Solution 2 - F#

I found out easier to just decompile what's happening, so:

type Region() =
  let mutable t = 0.0f
  member val Width = 0.0f
  member x.Height = 0.0f
  member val Left = 0.0f with get,set
  member x.Top with get() = 0.0f and set(value) = t <- value

is actually the following:

public class Region
{
	internal float t;

	internal float Width@;

	internal float Left@;

	public float Width
	{
		get
		{
			return this.Width@;
		}
	}

	public float Height
	{
		get
		{
			return 0f;
		}
	}

	public float Left
	{
		get
		{
			return this.Left@;
		}
		set
		{
			this.Left@ = value;
		}
	}

	public float Top
	{
		get
		{
			return 0f;
		}
		set
		{
			this.t = value;
		}
	}

	public Region() : this()
	{
		this.t = 0f;
		this.Width@ = 0f;
		this.Left@ = 0f;
	}
}

Solution 3 - F#

This sample explains the difference between syntaxes:

type MyClass() =
    let random  = new System.Random() 
    [<DefaultValue>] val mutable field : int
    member val AutoProperty = random.Next() with get, set
    member this.ExplicitProperty = random.Next()

let c = new MyClass()
// c.random is not accessible
c.field <- 42 // 'field' is accessible

// An automatic property is only evaluated upon initialization, and not every time the property is accessed
printfn "AutoProperty = %d" c.AutoProperty // x
printfn "AutoProperty = %d" c.AutoProperty // Still x

// The value of the explicit property is evaluated each time
printfn "ExplicitProperty = %d" c.ExplicitProperty // y
printfn "ExplicitProperty = %d" c.ExplicitProperty // The value is re-evaluated so you'll get a different value
    

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1804599View Question on Stackoverflow
Solution 1 - F#Tomas PetricekView Answer on Stackoverflow
Solution 2 - F#foobarcodeView Answer on Stackoverflow
Solution 3 - F#meziantouView Answer on Stackoverflow