Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference in behaviour between primary constructors in F# classes and structs?

If I try this type definition in Visual Studio Express 2013, I get an error:

type Foo(bar : int) =
   struct
      member x.bar = bar
   end

If I change it so that it is a class instead, I don't get any errors:

type Foo(bar : int) =
   class
      member x.bar = bar
   end

The error I get is:

The member 'bar' can not be defined because the name 'bar' clashes with the field 'bar' in this type or module"

The error goes away if I change the constructor parameter's name slightly. I understand the differences between CLR structs and classes in general, but I don't quite understand the reason for this difference in behaviour. Can anyone explain it?

I'm mostly asking in case the answer dispels some deeper misunderstanding that I have about F#. Thanks.

like image 242
HillwoodMonkey Avatar asked Mar 27 '14 18:03

HillwoodMonkey


1 Answers

Although I can't find a pertinent reference in the spec offhand, a little experimentation with a decompiler shows that primary constructor parameters on structs are always compiled as fields with the same name. For classes, a more complex set of rules (see §8.6.1.3) is used to determine their compiled form, which includes munging field names when they would otherwise clash with members.

Compare the compiled forms of this struct and class:

type S(bar: int) = struct end

type C(bar: int) =
    member x.bar = bar
public class C
{
    internal int bar@12;

    public int bar
    {
        get
        {
            return this.bar@12;
        }
    }

    public C(int bar)
    {
        this.bar@12 = bar;
    }
}

public struct S : IEquatable<Program.S>, IStructuralEquatable, IComparable<Program.S>, IComparable, IStructuralComparable
{
    internal int bar;

    public S(int bar)
    {
        this.bar = bar;
    }
}

The struct's primary constructor parameter is compiled as a field (and assigned!) even though it's unused. The class, on the other hand, allows a member and parameter with the same name. Admittedly, this doesn't explain why, but may help to clarify the observed behavior.

like image 166
Daniel Avatar answered Oct 14 '22 19:10

Daniel