AlexanderM
AlexanderM

Reputation: 1683

How to use methods to set a property in the default constructor in F#

I am trying to write a constructor that suppose to call method defined in the class and assign result to the member. In C# it would be something like:

public class Test
{
    public int Value { get; private set;}

    private static int SomeLogic(int a, int b)
    {
        return a + b;
    }

    public Test(int a, int b)
    {
        this.Value = SomeLogic(a,b);
    }
}

But I have no idea how to do that it F#.

Upvotes: 4

Views: 1303

Answers (3)

sgtz
sgtz

Reputation: 9019

type Test private (constructorParam) =

    let mutable value = constructorParam

    member this.Value
      with get()                = value 
       and private set newValue = value <- newValue

    static member SomeLogic(a,b) = a+b
    
    new(a,b) = Test(Test.SomeLogic(a,b))

let a = Test(1,2)

Note that the single param constructor is private. Consequently let a = Test(1) is not possible, as desired.

Maybe there's no need for a setter or a mutable field. If so...

type Test2 private (constructorParam) =

     let value = constructorParam    

     member this.Value with get() = value 

     static member SomeLogic(a,b) = a+b

     new(a,b) = Test2(Test.SomeLogic(a,b))
 
 let b = Test2(1,2)

references:

Upvotes: 3

Example of two different ways of achieving this in F#:

type Test_UsesVal =
  val value : int
  new (a, b) = { value = someLogic a b }
  member x.Value = x.value

type Test_Preferred (a : int, b: int) =
  let value = someLogic a b
  member x.Value = value

Two examples with drawbacks:

// This type is default constructible and when using default ctor someLogic is not used
type Test_DefaultConstructible () =
  let mutable value = 0
  new (a, b) as x = Test_DefaultConstructible () then x.Value <- someLogic a b
  member x.Value with get () = value and private set v = value <- v

// This type has 2 constructors and using single value ctor someLogic is not used
type Test_2Constructors (value : int) =
  new (a, b) = Test_2Constructors (someLogic a b)
  member x.Value with get () = value

As self referential types were mentioned I would just like to point out that making a type self referential adds hidden overhead:

// Self referential types add hidden overhead
type Test_SelfReferential (a : int, b: int) as this =
  let computeValue () = someLogic this.A this.B

  member x.A      = a
  member x.B      = b
  member x.Value  = computeValue ()

Decompiled the overhead shows:

[CompilationMapping(SourceConstructFlags.ObjectType)]
[Serializable]
public class Test_SelfReferential
{
  internal int b;

  internal int a;

  // An extra field added
  internal FSharpRef<Program.Test_SelfReferential> @this = new FSharpRef<Program.Test_SelfReferential>(null);

  // An extra field added
  internal int init@29-1;

  public int A
  {
    get
    {
      // An extra check added in each method 
      if (this.init@29-1 < 1)
      {
        LanguagePrimitives.IntrinsicFunctions.FailInit();
      }
      return this.a;
    }
  }

  public int B
  {
    get
    {
      // An extra check added in each method 
      if (this.init@29-1 < 1)
      {
        LanguagePrimitives.IntrinsicFunctions.FailInit();
      }
      return this.b;
    }
  }

  public int Value
  {
    get
    {
      // An extra check added in each method 
      if (this.init@29-1 < 1)
      {
        LanguagePrimitives.IntrinsicFunctions.FailInit();
      }
      return this.computeValue();
    }
  }

  public Test_SelfReferential(int a, int b) : this()
  {
    this.a = a;
    this.b = b;
    // An extra init added in .ctor
    [email protected] = this;
    this.init@29-1 = 1;
  }

  [CompilerGenerated]
  internal int computeValue()
  {
    // Extra checks added
    return LanguagePrimitives.IntrinsicFunctions.CheckThis<Program.Test_SelfReferential>([email protected]).A + LanguagePrimitives.IntrinsicFunctions.CheckThis<Program.Test_SelfReferential>([email protected]).B;
  }
}

The hidden overhead from self referential types in F# once caused me a performance regression (in some use-cases performance dropped by 50%).

Upvotes: 7

Bartek Kobyłecki
Bartek Kobyłecki

Reputation: 2395

You can use as binding at the beginning of the constructor:

type SomeClass() as this =

    do printfn this.Member1

    member this.Member1 = "s"

You may call it whatever you like: x, etc. but I think using just this makes sense here.

You can read more about Self Identifiers here: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/classes

EDIT: If you need to initialize any private fields of the type, you can define function in the constructor, use it in the initialization and then expose it as public member:

let func x = ...
let field = func 0
member this.Method(x) = func x

Upvotes: 2

Related Questions