Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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#.

like image 516
AlexanderM Avatar asked Dec 13 '22 22:12

AlexanderM


1 Answers

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

like image 191
Just another metaprogrammer Avatar answered Feb 15 '23 23:02

Just another metaprogrammer